<commit_before>
// -*- mode: c++; c-basic-offset:4 -*-

// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.

// Copyright (c) 2013 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.

#include <cppunit/TextTestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/extensions/HelperMacros.h>

//#define DODS_DEBUG

#include <fcntl.h>
#include <stdio.h>

#include <iostream>
#include <fstream>
#include <string>

#include "GetOpt.h"

#include "chunked_ostream.h"
#include "chunked_istream.h"

#include "InternalErr.h"
#include "test_config.h"
#include "debug.h"

static bool debug = false;

#undef DBG
#define DBG(x) do { if (debug) (x); } while(false);

using namespace std;
using namespace CppUnit;
using namespace libdap;

/**
 * The intent is to test writing to and reading from a chunked iostream,
 * using various combinations of chunk/buffer sizes and character red/write
 * sizes. There are three write functions and three read functions and
 * all combinations are tested.
 */
class chunked_iostream_test: public TestFixture {
private:
	// This should be big enough to do meaningful timing tests
	string big_file;
	// This should be smaller than a single buffer
	string small_file;
	// A modest sized text file - makes looking at the results easier
	string text_file;
public:
    chunked_iostream_test()
    {
    }
    ~chunked_iostream_test()
    {
    }

    void setUp()
    {
    	big_file = "test_big_binary_file.bin";
    	small_file = "test_small_text_file.txt";
    	text_file = "test_text_file.txt";
    }

    void tearDown()
    {
    }

    void
    single_char_write(const string &file, int buf_size)
    {
    	fstream infile(file.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");

    	string out = file + ".chunked";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	chunked_ostream chunked_outfile(outfile, buf_size);

    	char c;
    	infile.read(&c, 1);
    	int num = infile.gcount();
    	while (num > 0 && !infile.eof()) {
    		chunked_outfile.write(&c, num);
    		infile.read(&c, 1);
    		num = infile.gcount();
    	}

    	if (num > 0 && !infile.bad()) {
    		chunked_outfile.write(&c, num);
    	}

    	chunked_outfile.flush();
    }

    void
    write_128char_data(const string &file, int buf_size)
    {
    	fstream infile(file.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");

    	string out = file + ".chunked";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	chunked_ostream chunked_outfile(outfile, buf_size);

    	char str[128];
    	infile.read(str, 128);
    	int num = infile.gcount();
    	while (num > 0 && !infile.eof()) {
    		chunked_outfile.write(str, num);
    		infile.read(str, 128);
    		num = infile.gcount();
    	}

    	if (num > 0 && !infile.bad()) {
    		chunked_outfile.write(str, num);
    	}

        chunked_outfile.flush();
    }

    void
    write_24char_data_with_error_option(const string &file, int buf_size, bool error = false)
    {
    	fstream infile(file.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");

    	string out = file + ".chunked";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	chunked_ostream chunked_outfile(outfile, buf_size);

    	try {
    		char str[24];
    		infile.read(str, 24);
    		int num = infile.gcount();
    		if (num > 0 && !infile.eof()) {
    			chunked_outfile.write(str, num);
    			chunked_outfile.flush();
    		}

    		infile.read(str, 24);
    		num = infile.gcount();
    		if (num > 0 && !infile.eof()) chunked_outfile.write(str, num);

    		// Send an error chunk; the 24 bytes read here are lost...
    		if (error)
    			throw InternalErr(__FILE__, __LINE__, "Testing error transmission");

    		infile.read(str, 24);
    		num = infile.gcount();
    		while (num > 0 && !infile.eof()) {
    			chunked_outfile.write(str, num);
    			infile.read(str, 24);
    			num = infile.gcount();
    		}

    		if (num > 0 && !infile.bad()) {
    			chunked_outfile.write(str, num);
    		}

            chunked_outfile.flush();
    	}
    	catch (Error &e) {
    		chunked_outfile.write_err_chunk(e.get_error_message());
    	}
    }

    void
    single_char_read(const string &file, int buf_size)
    {
    	string in = file + ".chunked";
    	fstream infile(in.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");
    	chunked_istream chunked_infile(infile, buf_size, 0x00);

    	string out = file + ".plain";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	char c;
    	int count = 1;
    	chunked_infile.read(&c, 1);
    	int num = chunked_infile.gcount();
    	DBG(cerr << "num: " << count++ << endl);
    	while (num > 0 && !chunked_infile.eof()) {
    		outfile.write(&c, num);
    		chunked_infile.read(&c, 1);
    		num = chunked_infile.gcount();
            DBG(cerr << "num: " << count++ << endl);
    	}

    	if (num > 0 && !chunked_infile.bad())
    	    outfile.write(&c, num);

    	outfile.flush();
    }

    void
    read_128char_data(const string &file, int buf_size)
    {
    	string in = file + ".chunked";
    	fstream infile(in.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		cerr << "File not open or eof" << endl;
    	chunked_istream chunked_infile(infile, buf_size);

    	string out = file + ".plain";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	char str[128];
    	int count = 1;
    	chunked_infile.read(str, 128);
    	int num = chunked_infile.gcount();
    	DBG(cerr << "num: " << num << ", " << count++ << endl);
    	while (num > 0 && !chunked_infile.eof()) {
    		outfile.write(str, num);
    		chunked_infile.read(str, 128);
    		num = chunked_infile.gcount();
    		DBG(cerr << "num: " << num << ", " <<  count++ << endl);
    	}

    	if (num > 0 && !chunked_infile.bad()) {
    		outfile.write(str, num);
    	}

    	outfile.flush();
    }

    void
    read_24char_data_with_error_option(const string &file, int buf_size)
    {
    	string in = file + ".chunked";
    	fstream infile(in.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		cerr << "File not open or eof" << endl;
    	chunked_istream chunked_infile(infile, buf_size);

    	string out = file + ".plain";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	try {
    		char str[24];
    		chunked_infile.read(str, 24);
    		int num = chunked_infile.gcount();
    		if (num > 0 && !chunked_infile.eof()) {
    			outfile.write(str, num);
    			outfile.flush();
    		}

    		chunked_infile.read(str, 24);
    		num = chunked_infile.gcount();
    		while (num > 0 && !chunked_infile.eof()) {
    			outfile.write(str, num);
    			chunked_infile.read(str, 24);
    			num = chunked_infile.gcount();
    		}

    		if (num > 0 && !chunked_infile.bad()) {
    			outfile.write(str, num);
    		}

    		outfile.flush();
    	}
    	catch (Error &e) {
    		cerr << "Error chunk found: " << e.get_error_message() << endl;
    	}
    }

    // these are the tests
    void test_write_1_read_1_small_file() {
    	single_char_write(small_file, 32);
    	single_char_read(small_file, 32);
    	string cmp = "cmp " + small_file + " " + small_file + ".plain";
    	CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_1_text_file() {
    	single_char_write(text_file, 32);
    	single_char_read(text_file, 32);
    	string cmp = "cmp " + text_file + " " + text_file + ".plain";
    	CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_1_big_file() {
    	single_char_write(big_file, 28);
    	single_char_read(big_file, 28);
    	string cmp = "cmp " + big_file + " " + big_file + ".plain";
    	CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    // these are the tests
    void test_write_1_read_128_small_file() {
        single_char_write(small_file, 32);
        read_128char_data(small_file, 32);
        string cmp = "cmp " + small_file + " " + small_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_128_text_file() {
        single_char_write(text_file, 32);
        read_128char_data(text_file, 32);
        string cmp = "cmp " + text_file + " " + text_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_128_big_file() {
        single_char_write(big_file, 28);
        read_128char_data(big_file, 28);
        string cmp = "cmp " + big_file + " " + big_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    CPPUNIT_TEST_SUITE(chunked_iostream_test);

    CPPUNIT_TEST(test_write_1_read_1_small_file);
    CPPUNIT_TEST(test_write_1_read_1_text_file);
    CPPUNIT_TEST(test_write_1_read_1_big_file);

    CPPUNIT_TEST(test_write_1_read_128_small_file);
    CPPUNIT_TEST(test_write_1_read_128_text_file);
    CPPUNIT_TEST(test_write_1_read_128_big_file);

    CPPUNIT_TEST_SUITE_END();
};

CPPUNIT_TEST_SUITE_REGISTRATION(chunked_iostream_test);

int
main(int argc, char *argv[])
{
    GetOpt getopt(argc, argv, "d");
    char option_char;

    while ((option_char = getopt()) != EOF)
        switch (option_char) {
            case 'd':
                debug = 1;  // debug is a static global
                break;
            default:
                break;
        }

    CppUnit::TextTestRunner runner;
    runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());

    bool wasSuccessful = true;
    string test = "";
    int i = getopt.optind;
    if (i == argc) {
        // run them all
        wasSuccessful = runner.run("");
    }
    else {
        while (i < argc) {
            test = string("chunked_iostream_test::") + argv[i++];
            if (debug)
                cerr << "Running " << test << endl;
            wasSuccessful = wasSuccessful && runner.run(test);
        }
    }

    return wasSuccessful ? 0 : 1;
}
<commit_msg>added big_file_2 tests<commit_after>
// -*- mode: c++; c-basic-offset:4 -*-

// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.

// Copyright (c) 2013 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.

#include <cppunit/TextTestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/extensions/HelperMacros.h>

//#define DODS_DEBUG

#include <fcntl.h>
#include <stdio.h>

#include <iostream>
#include <fstream>
#include <string>

#include "GetOpt.h"

#include "chunked_ostream.h"
#include "chunked_istream.h"

#include "InternalErr.h"
#include "test_config.h"
#include "debug.h"

static bool debug = false;

#undef DBG
#define DBG(x) do { if (debug) (x); } while(false);

using namespace std;
using namespace CppUnit;
using namespace libdap;

/**
 * The intent is to test writing to and reading from a chunked iostream,
 * using various combinations of chunk/buffer sizes and character red/write
 * sizes. There are three write functions and three read functions and
 * all combinations are tested.
 */
class chunked_iostream_test: public TestFixture {
private:
	// This should be big enough to do meaningful timing tests
	string big_file, big_file_2;
	// This should be smaller than a single buffer
	string small_file;
	// A modest sized text file - makes looking at the results easier
	string text_file;
public:
    chunked_iostream_test()
    {
    }
    ~chunked_iostream_test()
    {
    }

    void setUp()
    {
    	big_file = "test_big_binary_file.bin";
    	big_file_2 = "test_big_binary_file_2.bin";
    	small_file = "test_small_text_file.txt";
    	text_file = "test_text_file.txt";
    }

    void tearDown()
    {
    }

    void
    single_char_write(const string &file, int buf_size)
    {
    	fstream infile(file.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");

    	string out = file + ".chunked";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	chunked_ostream chunked_outfile(outfile, buf_size);

    	char c;
    	infile.read(&c, 1);
    	int num = infile.gcount();
    	while (num > 0 && !infile.eof()) {
    		chunked_outfile.write(&c, num);
    		infile.read(&c, 1);
    		num = infile.gcount();
    	}

    	if (num > 0 && !infile.bad()) {
    		chunked_outfile.write(&c, num);
    	}

    	chunked_outfile.flush();
    }

    void
    write_128char_data(const string &file, int buf_size)
    {
    	fstream infile(file.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");

    	string out = file + ".chunked";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	chunked_ostream chunked_outfile(outfile, buf_size);

    	char str[128];
    	infile.read(str, 128);
    	int num = infile.gcount();
    	while (num > 0 && !infile.eof()) {
    		chunked_outfile.write(str, num);
    		infile.read(str, 128);
    		num = infile.gcount();
    	}

    	if (num > 0 && !infile.bad()) {
    		chunked_outfile.write(str, num);
    	}

        chunked_outfile.flush();
    }

    void
    write_24char_data_with_error_option(const string &file, int buf_size, bool error = false)
    {
    	fstream infile(file.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");

    	string out = file + ".chunked";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	chunked_ostream chunked_outfile(outfile, buf_size);

    	try {
    		char str[24];
    		infile.read(str, 24);
    		int num = infile.gcount();
    		if (num > 0 && !infile.eof()) {
    			chunked_outfile.write(str, num);
    			chunked_outfile.flush();
    		}

    		infile.read(str, 24);
    		num = infile.gcount();
    		if (num > 0 && !infile.eof()) chunked_outfile.write(str, num);

    		// Send an error chunk; the 24 bytes read here are lost...
    		if (error)
    			throw InternalErr(__FILE__, __LINE__, "Testing error transmission");

    		infile.read(str, 24);
    		num = infile.gcount();
    		while (num > 0 && !infile.eof()) {
    			chunked_outfile.write(str, num);
    			infile.read(str, 24);
    			num = infile.gcount();
    		}

    		if (num > 0 && !infile.bad()) {
    			chunked_outfile.write(str, num);
    		}

            chunked_outfile.flush();
    	}
    	catch (Error &e) {
    		chunked_outfile.write_err_chunk(e.get_error_message());
    	}
    }

    void
    single_char_read(const string &file, int buf_size)
    {
    	string in = file + ".chunked";
    	fstream infile(in.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		CPPUNIT_FAIL("File not open or eof");
    	chunked_istream chunked_infile(infile, buf_size, 0x00);

    	string out = file + ".plain";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	char c;
    	int count = 1;
    	chunked_infile.read(&c, 1);
    	int num = chunked_infile.gcount();
    	DBG(cerr << "num: " << num << ", " << count++ << endl);
    	while (num > 0 && !chunked_infile.eof()) {
    		outfile.write(&c, num);
    		chunked_infile.read(&c, 1);
    		num = chunked_infile.gcount();
            DBG(cerr << "num: " << num << ", " << count++ << endl);
    	}

    	DBG(cerr << "eof is :" << chunked_infile.eof() << ", num: " << num << endl);

    	if (num > 0 && !chunked_infile.bad())
    	    outfile.write(&c, num);

    	outfile.flush();
    }

    void
    read_128char_data(const string &file, int buf_size)
    {
    	string in = file + ".chunked";
    	fstream infile(in.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		cerr << "File not open or eof" << endl;
    	chunked_istream chunked_infile(infile, buf_size);

    	string out = file + ".plain";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	char str[128];
    	int count = 1;
    	chunked_infile.read(str, 128);
    	int num = chunked_infile.gcount();
    	DBG(cerr << "num: " << num << ", " << count++ << endl);
    	while (num > 0 && !chunked_infile.eof()) {
    		outfile.write(str, num);
    		chunked_infile.read(str, 128);
    		num = chunked_infile.gcount();
    		DBG(cerr << "num: " << num << ", " <<  count++ << endl);
    	}

    	if (num > 0 && !chunked_infile.bad()) {
    		outfile.write(str, num);
    	}

    	outfile.flush();
    }

    void
    read_24char_data_with_error_option(const string &file, int buf_size)
    {
    	string in = file + ".chunked";
    	fstream infile(in.c_str(), ios::in|ios::binary);
    	if (!infile.good())
    		cerr << "File not open or eof" << endl;
    	chunked_istream chunked_infile(infile, buf_size);

    	string out = file + ".plain";
    	fstream outfile(out.c_str(), ios::out|ios::binary);

    	try {
    		char str[24];
    		chunked_infile.read(str, 24);
    		int num = chunked_infile.gcount();
    		if (num > 0 && !chunked_infile.eof()) {
    			outfile.write(str, num);
    			outfile.flush();
    		}

    		chunked_infile.read(str, 24);
    		num = chunked_infile.gcount();
    		while (num > 0 && !chunked_infile.eof()) {
    			outfile.write(str, num);
    			chunked_infile.read(str, 24);
    			num = chunked_infile.gcount();
    		}

    		if (num > 0 && !chunked_infile.bad()) {
    			outfile.write(str, num);
    		}

    		outfile.flush();
    	}
    	catch (Error &e) {
    		cerr << "Error chunk found: " << e.get_error_message() << endl;
    	}
    }

    // these are the tests
    void test_write_1_read_1_small_file() {
    	single_char_write(small_file, 32);
    	single_char_read(small_file, 32);
    	string cmp = "cmp " + small_file + " " + small_file + ".plain";
    	CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_1_text_file() {
    	single_char_write(text_file, 32);
    	single_char_read(text_file, 32);
    	string cmp = "cmp " + text_file + " " + text_file + ".plain";
    	CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_1_big_file() {
        single_char_write(big_file, 28);
        single_char_read(big_file, 28);
        string cmp = "cmp " + big_file + " " + big_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }
    void test_write_1_read_1_big_file_2() {
        single_char_write(big_file_2, 28);
        single_char_read(big_file_2, 28);
        string cmp = "cmp " + big_file_2 + " " + big_file_2 + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    // these are the tests
    void test_write_1_read_128_small_file() {
        single_char_write(small_file, 32);
        read_128char_data(small_file, 32);
        string cmp = "cmp " + small_file + " " + small_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_128_text_file() {
        single_char_write(text_file, 32);
        read_128char_data(text_file, 32);
        string cmp = "cmp " + text_file + " " + text_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_128_big_file() {
        single_char_write(big_file, 28);
        read_128char_data(big_file, 28);
        string cmp = "cmp " + big_file + " " + big_file + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    void test_write_1_read_128_big_file_2() {
        single_char_write(big_file_2, 28);
        read_128char_data(big_file_2, 28);
        string cmp = "cmp " + big_file_2 + " " + big_file_2 + ".plain";
        CPPUNIT_ASSERT(system(cmp.c_str()) == 0);
    }

    CPPUNIT_TEST_SUITE(chunked_iostream_test);

    CPPUNIT_TEST(test_write_1_read_1_small_file);
    CPPUNIT_TEST(test_write_1_read_1_text_file);
    CPPUNIT_TEST(test_write_1_read_1_big_file);
    CPPUNIT_TEST(test_write_1_read_1_big_file_2);

    CPPUNIT_TEST(test_write_1_read_128_small_file);
    CPPUNIT_TEST(test_write_1_read_128_text_file);
    CPPUNIT_TEST(test_write_1_read_128_big_file);
    CPPUNIT_TEST(test_write_1_read_128_big_file_2);

    CPPUNIT_TEST_SUITE_END();
};

CPPUNIT_TEST_SUITE_REGISTRATION(chunked_iostream_test);

int
main(int argc, char *argv[])
{
    GetOpt getopt(argc, argv, "d");
    char option_char;

    while ((option_char = getopt()) != EOF)
        switch (option_char) {
            case 'd':
                debug = 1;  // debug is a static global
                break;
            default:
                break;
        }

    CppUnit::TextTestRunner runner;
    runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());

    bool wasSuccessful = true;
    string test = "";
    int i = getopt.optind;
    if (i == argc) {
        // run them all
        wasSuccessful = runner.run("");
    }
    else {
        while (i < argc) {
            test = string("chunked_iostream_test::") + argv[i++];
            if (debug)
                cerr << "Running " << test << endl;
            wasSuccessful = wasSuccessful && runner.run(test);
        }
    }

    return wasSuccessful ? 0 : 1;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006-2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebInputEventFactory.h"

#include "WebInputEvent.h"

#include <wtf/Assertions.h>

namespace WebKit {

static const unsigned long defaultScrollLinesPerWheelDelta = 3;
static const unsigned long defaultScrollCharsPerWheelDelta = 1;

// WebKeyboardEvent -----------------------------------------------------------

static bool isKeyPad(WPARAM wparam, LPARAM lparam)
{
    bool keypad = false;
    switch (wparam) {
    case VK_RETURN:
        keypad = (lparam >> 16) & KF_EXTENDED;
        break;
    case VK_INSERT:
    case VK_DELETE:
    case VK_HOME:
    case VK_END:
    case VK_PRIOR:
    case VK_NEXT:
    case VK_UP:
    case VK_DOWN:
    case VK_LEFT:
    case VK_RIGHT:
        keypad = !((lparam >> 16) & KF_EXTENDED);
        break;
    case VK_NUMLOCK:
    case VK_NUMPAD0:
    case VK_NUMPAD1:
    case VK_NUMPAD2:
    case VK_NUMPAD3:
    case VK_NUMPAD4:
    case VK_NUMPAD5:
    case VK_NUMPAD6:
    case VK_NUMPAD7:
    case VK_NUMPAD8:
    case VK_NUMPAD9:
    case VK_DIVIDE:
    case VK_MULTIPLY:
    case VK_SUBTRACT:
    case VK_ADD:
    case VK_DECIMAL:
    case VK_CLEAR:
        keypad = true;
        break;
    default:
        keypad = false;
    }
    return keypad;
}

WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message,
                                                     WPARAM wparam, LPARAM lparam)
{
    WebKeyboardEvent result;

    result.windowsKeyCode = result.nativeKeyCode = static_cast<int>(wparam);

    switch (message) {
    case WM_SYSKEYDOWN:
        result.isSystemKey = true;
    case WM_KEYDOWN:
        result.type = WebInputEvent::RawKeyDown;
        break;
    case WM_SYSKEYUP:
        result.isSystemKey = true;
    case WM_KEYUP:
        result.type = WebInputEvent::KeyUp;
        break;
    case WM_IME_CHAR:
        result.type = WebInputEvent::Char;
        break;
    case WM_SYSCHAR:
        result.isSystemKey = true;
        result.type = WebInputEvent::Char;
    case WM_CHAR:
        result.type = WebInputEvent::Char;
        break;
    default:
        ASSERT_NOT_REACHED();
    }

    if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) {
        result.text[0] = result.windowsKeyCode;
        result.unmodifiedText[0] = result.windowsKeyCode;
    }
    if (result.type != WebInputEvent::Char)
        result.setKeyIdentifierFromWindowsKeyCode();

    if (GetKeyState(VK_SHIFT) & 0x8000)
        result.modifiers |= WebInputEvent::ShiftKey;
    if (GetKeyState(VK_CONTROL) & 0x8000)
        result.modifiers |= WebInputEvent::ControlKey;
    if (GetKeyState(VK_MENU) & 0x8000)
        result.modifiers |= (WebInputEvent::AltKey | WebInputEvent::MetaKey);

    if (LOWORD(lparam) > 1)
        result.modifiers |= WebInputEvent::IsAutoRepeat;
    if (isKeyPad(wparam, lparam))
        result.modifiers |= WebInputEvent::IsKeyPad;

    return result;
}

// WebMouseEvent --------------------------------------------------------------

static int gLastClickCount;
static double gLastClickTime;

static LPARAM GetRelativeCursorPos(HWND hwnd)
{
    POINT pos = {-1, -1};
    GetCursorPos(&pos);
    ScreenToClient(hwnd, &pos);
    return MAKELPARAM(pos.x, pos.y);
}

void WebInputEventFactory::resetLastClickState()
{
    gLastClickTime = gLastClickCount = 0;
}

WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message,
                                               WPARAM wparam, LPARAM lparam)
{
    WebMouseEvent result; //(WebInputEvent::Uninitialized());

    switch (message) {
    case WM_MOUSEMOVE:
        result.type = WebInputEvent::MouseMove;
        if (wparam & MK_LBUTTON)
            result.button = WebMouseEvent::ButtonLeft;
        else if (wparam & MK_MBUTTON)
            result.button = WebMouseEvent::ButtonMiddle;
        else if (wparam & MK_RBUTTON)
            result.button = WebMouseEvent::ButtonMiddle;
        else
            result.button = WebMouseEvent::ButtonNone;
        break;
    case WM_MOUSELEAVE:
        result.type = WebInputEvent::MouseLeave;
        result.button = WebMouseEvent::ButtonNone;
        // set the current mouse position (relative to the client area of the
        // current window) since none is specified for this event
        lparam = GetRelativeCursorPos(hwnd);
        break;
    case WM_LBUTTONDOWN:
    case WM_LBUTTONDBLCLK:
        result.type = WebInputEvent::MouseDown;
        result.button = WebMouseEvent::ButtonLeft;
        break;
    case WM_MBUTTONDOWN:
    case WM_MBUTTONDBLCLK:
        result.type = WebInputEvent::MouseDown;
        result.button = WebMouseEvent::ButtonMiddle;
        break;
    case WM_RBUTTONDOWN:
    case WM_RBUTTONDBLCLK:
        result.type = WebInputEvent::MouseDown;
        result.button = WebMouseEvent::ButtonRight;
        break;
    case WM_LBUTTONUP:
        result.type = WebInputEvent::MouseUp;
        result.button = WebMouseEvent::ButtonLeft;
        break;
    case WM_MBUTTONUP:
        result.type = WebInputEvent::MouseUp;
        result.button = WebMouseEvent::ButtonMiddle;
        break;
    case WM_RBUTTONUP:
        result.type = WebInputEvent::MouseUp;
        result.button = WebMouseEvent::ButtonRight;
        break;
    default:
        ASSERT_NOT_REACHED();
    }

    // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
    // GetMessageTime() refers to is the same one that we're passed in? Perhaps
    // one of the construction parameters should be the time passed by the
    // caller, who would know for sure.
    result.timeStampSeconds = GetMessageTime() / 1000.0;

    // set position fields:

    result.x = static_cast<short>(LOWORD(lparam));
    result.y = static_cast<short>(HIWORD(lparam));

    POINT globalPoint = { result.x, result.y };
    ClientToScreen(hwnd, &globalPoint);

    result.globalX = globalPoint.x;
    result.globalY = globalPoint.y;

    // calculate number of clicks:

    // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
    // where their original code looks buggy.
    static int lastClickPositionX;
    static int lastClickPositionY;
    static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft;

    double currentTime = result.timeStampSeconds;
    bool cancelPreviousClick =
        (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2))
        || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2))
        || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime());

    if (result.type == WebInputEvent::MouseDown) {
        if (!cancelPreviousClick && (result.button == lastClickButton))
            ++gLastClickCount;
        else {
            gLastClickCount = 1;
            lastClickPositionX = result.x;
            lastClickPositionY = result.y;
        }
        gLastClickTime = currentTime;
        lastClickButton = result.button;
    } else if (result.type == WebInputEvent::MouseMove
               || result.type == WebInputEvent::MouseLeave) {
        if (cancelPreviousClick) {
            gLastClickCount = 0;
            lastClickPositionX = 0;
            lastClickPositionY = 0;
            gLastClickTime = 0;
        }
    }
    result.clickCount = gLastClickCount;

    // set modifiers:

    if (wparam & MK_CONTROL)
        result.modifiers |= WebInputEvent::ControlKey;
    if (wparam & MK_SHIFT)
        result.modifiers |= WebInputEvent::ShiftKey;
    if (GetKeyState(VK_MENU) & 0x8000)
        result.modifiers |= (WebInputEvent::AltKey | WebInputEvent::MetaKey);  // FIXME: set META properly

    return result;
}

// WebMouseWheelEvent ---------------------------------------------------------

WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message,
                                                         WPARAM wparam, LPARAM lparam)
{
    WebMouseWheelEvent result; //(WebInputEvent::Uninitialized());

    result.type = WebInputEvent::MouseWheel;
    result.button = WebMouseEvent::ButtonNone;

    // Get key state, coordinates, and wheel delta from event.
    typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
    GetKeyStateFunction getKeyState;
    UINT keyState;
    float wheelDelta;
    bool horizontalScroll = false;
    if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
        // Synthesize mousewheel event from a scroll event.  This is needed to
        // simulate middle mouse scrolling in some laptops.  Use GetAsyncKeyState
        // for key state since we are synthesizing the input event.
        getKeyState = GetAsyncKeyState;
        keyState = 0;
        if (getKeyState(VK_SHIFT))
            keyState |= MK_SHIFT;
        if (getKeyState(VK_CONTROL))
            keyState |= MK_CONTROL;

        POINT cursorPosition = {0};
        GetCursorPos(&cursorPosition);
        result.globalX = cursorPosition.x;
        result.globalY = cursorPosition.y;

        switch (LOWORD(wparam)) {
        case SB_LINEUP:    // == SB_LINELEFT
            wheelDelta = WHEEL_DELTA;
            break;
        case SB_LINEDOWN:  // == SB_LINERIGHT
            wheelDelta = -WHEEL_DELTA;
            break;
        case SB_PAGEUP:
            wheelDelta = 1;
            result.scrollByPage = true;
            break;
        case SB_PAGEDOWN:
            wheelDelta = -1;
            result.scrollByPage = true;
            break;
        default:  // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
            wheelDelta = 0;
            break;
        }

        if (message == WM_HSCROLL)
            horizontalScroll = true;
    } else {
        // Non-synthesized event; we can just read data off the event.
        getKeyState = GetKeyState;
        keyState = GET_KEYSTATE_WPARAM(wparam);

        result.globalX = static_cast<short>(LOWORD(lparam));
        result.globalY = static_cast<short>(HIWORD(lparam));

        wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
        if (message == WM_MOUSEHWHEEL) {
            horizontalScroll = true;
            wheelDelta = -wheelDelta;  // Windows is <- -/+ ->, WebKit <- +/- ->.
        }
    }
    if (keyState & MK_SHIFT)
        horizontalScroll = true;

    // Set modifiers based on key state.
    if (keyState & MK_SHIFT)
        result.modifiers |= WebInputEvent::ShiftKey;
    if (keyState & MK_CONTROL)
        result.modifiers |= WebInputEvent::ControlKey;
    if (getKeyState(VK_MENU) & 0x8000)
        result.modifiers |= (WebInputEvent::AltKey | WebInputEvent::MetaKey);

    // Set coordinates by translating event coordinates from screen to client.
    POINT clientPoint = { result.globalX, result.globalY };
    MapWindowPoints(NULL, hwnd, &clientPoint, 1);
    result.x = clientPoint.x;
    result.y = clientPoint.y;

    // Convert wheel delta amount to a number of pixels to scroll.
    //
    // How many pixels should we scroll per line?  Gecko uses the height of the
    // current line, which means scroll distance changes as you go through the
    // page or go to different pages.  IE 7 is ~50 px/line, although the value
    // seems to vary slightly by page and zoom level.  Since IE 7 has a smoothing
    // algorithm on scrolling, it can get away with slightly larger scroll values
    // without feeling jerky.  Here we use 100 px per three lines (the default
    // scroll amount is three lines per wheel tick).
    static const float scrollbarPixelsPerLine = 100.0f / 3.0f;
    wheelDelta /= WHEEL_DELTA;
    float scrollDelta = wheelDelta;
    if (horizontalScroll) {
        unsigned long scrollChars = defaultScrollCharsPerWheelDelta;
        SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
        // TODO(pkasting): Should probably have a different multiplier
        // scrollbarPixelsPerChar here.
        scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine;
    } else {
        unsigned long scrollLines = defaultScrollLinesPerWheelDelta;
        SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
        if (scrollLines == WHEEL_PAGESCROLL)
            result.scrollByPage = true;
        if (!result.scrollByPage)
            scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine;
    }

    // Set scroll amount based on above calculations.  WebKit expects positive
    // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
    if (horizontalScroll) {
        result.deltaX = scrollDelta;
        result.wheelTicksX = wheelDelta;
    } else {
        result.deltaY = scrollDelta;
        result.wheelTicksY = wheelDelta;
    }

    return result;
}

} // namespace WebKit
<commit_msg>Fix typo.  The right button is pressed when MK_RBUTTON is set.<commit_after>/*
 * Copyright (C) 2006-2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebInputEventFactory.h"

#include "WebInputEvent.h"

#include <wtf/Assertions.h>

namespace WebKit {

static const unsigned long defaultScrollLinesPerWheelDelta = 3;
static const unsigned long defaultScrollCharsPerWheelDelta = 1;

// WebKeyboardEvent -----------------------------------------------------------

static bool isKeyPad(WPARAM wparam, LPARAM lparam)
{
    bool keypad = false;
    switch (wparam) {
    case VK_RETURN:
        keypad = (lparam >> 16) & KF_EXTENDED;
        break;
    case VK_INSERT:
    case VK_DELETE:
    case VK_HOME:
    case VK_END:
    case VK_PRIOR:
    case VK_NEXT:
    case VK_UP:
    case VK_DOWN:
    case VK_LEFT:
    case VK_RIGHT:
        keypad = !((lparam >> 16) & KF_EXTENDED);
        break;
    case VK_NUMLOCK:
    case VK_NUMPAD0:
    case VK_NUMPAD1:
    case VK_NUMPAD2:
    case VK_NUMPAD3:
    case VK_NUMPAD4:
    case VK_NUMPAD5:
    case VK_NUMPAD6:
    case VK_NUMPAD7:
    case VK_NUMPAD8:
    case VK_NUMPAD9:
    case VK_DIVIDE:
    case VK_MULTIPLY:
    case VK_SUBTRACT:
    case VK_ADD:
    case VK_DECIMAL:
    case VK_CLEAR:
        keypad = true;
        break;
    default:
        keypad = false;
    }
    return keypad;
}

WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message,
                                                     WPARAM wparam, LPARAM lparam)
{
    WebKeyboardEvent result;

    result.windowsKeyCode = result.nativeKeyCode = static_cast<int>(wparam);

    switch (message) {
    case WM_SYSKEYDOWN:
        result.isSystemKey = true;
    case WM_KEYDOWN:
        result.type = WebInputEvent::RawKeyDown;
        break;
    case WM_SYSKEYUP:
        result.isSystemKey = true;
    case WM_KEYUP:
        result.type = WebInputEvent::KeyUp;
        break;
    case WM_IME_CHAR:
        result.type = WebInputEvent::Char;
        break;
    case WM_SYSCHAR:
        result.isSystemKey = true;
        result.type = WebInputEvent::Char;
    case WM_CHAR:
        result.type = WebInputEvent::Char;
        break;
    default:
        ASSERT_NOT_REACHED();
    }

    if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) {
        result.text[0] = result.windowsKeyCode;
        result.unmodifiedText[0] = result.windowsKeyCode;
    }
    if (result.type != WebInputEvent::Char)
        result.setKeyIdentifierFromWindowsKeyCode();

    if (GetKeyState(VK_SHIFT) & 0x8000)
        result.modifiers |= WebInputEvent::ShiftKey;
    if (GetKeyState(VK_CONTROL) & 0x8000)
        result.modifiers |= WebInputEvent::ControlKey;
    if (GetKeyState(VK_MENU) & 0x8000)
        result.modifiers |= (WebInputEvent::AltKey | WebInputEvent::MetaKey);

    if (LOWORD(lparam) > 1)
        result.modifiers |= WebInputEvent::IsAutoRepeat;
    if (isKeyPad(wparam, lparam))
        result.modifiers |= WebInputEvent::IsKeyPad;

    return result;
}

// WebMouseEvent --------------------------------------------------------------

static int gLastClickCount;
static double gLastClickTime;

static LPARAM GetRelativeCursorPos(HWND hwnd)
{
    POINT pos = {-1, -1};
    GetCursorPos(&pos);
    ScreenToClient(hwnd, &pos);
    return MAKELPARAM(pos.x, pos.y);
}

void WebInputEventFactory::resetLastClickState()
{
    gLastClickTime = gLastClickCount = 0;
}

WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message,
                                               WPARAM wparam, LPARAM lparam)
{
    WebMouseEvent result; //(WebInputEvent::Uninitialized());

    switch (message) {
    case WM_MOUSEMOVE:
        result.type = WebInputEvent::MouseMove;
        if (wparam & MK_LBUTTON)
            result.button = WebMouseEvent::ButtonLeft;
        else if (wparam & MK_MBUTTON)
            result.button = WebMouseEvent::ButtonMiddle;
        else if (wparam & MK_RBUTTON)
            result.button = WebMouseEvent::ButtonRight;
        else
            result.button = WebMouseEvent::ButtonNone;
        break;
    case WM_MOUSELEAVE:
        result.type = WebInputEvent::MouseLeave;
        result.button = WebMouseEvent::ButtonNone;
        // set the current mouse position (relative to the client area of the
        // current window) since none is specified for this event
        lparam = GetRelativeCursorPos(hwnd);
        break;
    case WM_LBUTTONDOWN:
    case WM_LBUTTONDBLCLK:
        result.type = WebInputEvent::MouseDown;
        result.button = WebMouseEvent::ButtonLeft;
        break;
    case WM_MBUTTONDOWN:
    case WM_MBUTTONDBLCLK:
        result.type = WebInputEvent::MouseDown;
        result.button = WebMouseEvent::ButtonMiddle;
        break;
    case WM_RBUTTONDOWN:
    case WM_RBUTTONDBLCLK:
        result.type = WebInputEvent::MouseDown;
        result.button = WebMouseEvent::ButtonRight;
        break;
    case WM_LBUTTONUP:
        result.type = WebInputEvent::MouseUp;
        result.button = WebMouseEvent::ButtonLeft;
        break;
    case WM_MBUTTONUP:
        result.type = WebInputEvent::MouseUp;
        result.button = WebMouseEvent::ButtonMiddle;
        break;
    case WM_RBUTTONUP:
        result.type = WebInputEvent::MouseUp;
        result.button = WebMouseEvent::ButtonRight;
        break;
    default:
        ASSERT_NOT_REACHED();
    }

    // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
    // GetMessageTime() refers to is the same one that we're passed in? Perhaps
    // one of the construction parameters should be the time passed by the
    // caller, who would know for sure.
    result.timeStampSeconds = GetMessageTime() / 1000.0;

    // set position fields:

    result.x = static_cast<short>(LOWORD(lparam));
    result.y = static_cast<short>(HIWORD(lparam));

    POINT globalPoint = { result.x, result.y };
    ClientToScreen(hwnd, &globalPoint);

    result.globalX = globalPoint.x;
    result.globalY = globalPoint.y;

    // calculate number of clicks:

    // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
    // where their original code looks buggy.
    static int lastClickPositionX;
    static int lastClickPositionY;
    static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft;

    double currentTime = result.timeStampSeconds;
    bool cancelPreviousClick =
        (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2))
        || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2))
        || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime());

    if (result.type == WebInputEvent::MouseDown) {
        if (!cancelPreviousClick && (result.button == lastClickButton))
            ++gLastClickCount;
        else {
            gLastClickCount = 1;
            lastClickPositionX = result.x;
            lastClickPositionY = result.y;
        }
        gLastClickTime = currentTime;
        lastClickButton = result.button;
    } else if (result.type == WebInputEvent::MouseMove
               || result.type == WebInputEvent::MouseLeave) {
        if (cancelPreviousClick) {
            gLastClickCount = 0;
            lastClickPositionX = 0;
            lastClickPositionY = 0;
            gLastClickTime = 0;
        }
    }
    result.clickCount = gLastClickCount;

    // set modifiers:

    if (wparam & MK_CONTROL)
        result.modifiers |= WebInputEvent::ControlKey;
    if (wparam & MK_SHIFT)
        result.modifiers |= WebInputEvent::ShiftKey;
    if (GetKeyState(VK_MENU) & 0x8000)
        result.modifiers |= (WebInputEvent::AltKey | WebInputEvent::MetaKey);  // FIXME: set META properly

    return result;
}

// WebMouseWheelEvent ---------------------------------------------------------

WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message,
                                                         WPARAM wparam, LPARAM lparam)
{
    WebMouseWheelEvent result; //(WebInputEvent::Uninitialized());

    result.type = WebInputEvent::MouseWheel;
    result.button = WebMouseEvent::ButtonNone;

    // Get key state, coordinates, and wheel delta from event.
    typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
    GetKeyStateFunction getKeyState;
    UINT keyState;
    float wheelDelta;
    bool horizontalScroll = false;
    if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
        // Synthesize mousewheel event from a scroll event.  This is needed to
        // simulate middle mouse scrolling in some laptops.  Use GetAsyncKeyState
        // for key state since we are synthesizing the input event.
        getKeyState = GetAsyncKeyState;
        keyState = 0;
        if (getKeyState(VK_SHIFT))
            keyState |= MK_SHIFT;
        if (getKeyState(VK_CONTROL))
            keyState |= MK_CONTROL;

        POINT cursorPosition = {0};
        GetCursorPos(&cursorPosition);
        result.globalX = cursorPosition.x;
        result.globalY = cursorPosition.y;

        switch (LOWORD(wparam)) {
        case SB_LINEUP:    // == SB_LINELEFT
            wheelDelta = WHEEL_DELTA;
            break;
        case SB_LINEDOWN:  // == SB_LINERIGHT
            wheelDelta = -WHEEL_DELTA;
            break;
        case SB_PAGEUP:
            wheelDelta = 1;
            result.scrollByPage = true;
            break;
        case SB_PAGEDOWN:
            wheelDelta = -1;
            result.scrollByPage = true;
            break;
        default:  // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
            wheelDelta = 0;
            break;
        }

        if (message == WM_HSCROLL)
            horizontalScroll = true;
    } else {
        // Non-synthesized event; we can just read data off the event.
        getKeyState = GetKeyState;
        keyState = GET_KEYSTATE_WPARAM(wparam);

        result.globalX = static_cast<short>(LOWORD(lparam));
        result.globalY = static_cast<short>(HIWORD(lparam));

        wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
        if (message == WM_MOUSEHWHEEL) {
            horizontalScroll = true;
            wheelDelta = -wheelDelta;  // Windows is <- -/+ ->, WebKit <- +/- ->.
        }
    }
    if (keyState & MK_SHIFT)
        horizontalScroll = true;

    // Set modifiers based on key state.
    if (keyState & MK_SHIFT)
        result.modifiers |= WebInputEvent::ShiftKey;
    if (keyState & MK_CONTROL)
        result.modifiers |= WebInputEvent::ControlKey;
    if (getKeyState(VK_MENU) & 0x8000)
        result.modifiers |= (WebInputEvent::AltKey | WebInputEvent::MetaKey);

    // Set coordinates by translating event coordinates from screen to client.
    POINT clientPoint = { result.globalX, result.globalY };
    MapWindowPoints(NULL, hwnd, &clientPoint, 1);
    result.x = clientPoint.x;
    result.y = clientPoint.y;

    // Convert wheel delta amount to a number of pixels to scroll.
    //
    // How many pixels should we scroll per line?  Gecko uses the height of the
    // current line, which means scroll distance changes as you go through the
    // page or go to different pages.  IE 7 is ~50 px/line, although the value
    // seems to vary slightly by page and zoom level.  Since IE 7 has a smoothing
    // algorithm on scrolling, it can get away with slightly larger scroll values
    // without feeling jerky.  Here we use 100 px per three lines (the default
    // scroll amount is three lines per wheel tick).
    static const float scrollbarPixelsPerLine = 100.0f / 3.0f;
    wheelDelta /= WHEEL_DELTA;
    float scrollDelta = wheelDelta;
    if (horizontalScroll) {
        unsigned long scrollChars = defaultScrollCharsPerWheelDelta;
        SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
        // TODO(pkasting): Should probably have a different multiplier
        // scrollbarPixelsPerChar here.
        scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine;
    } else {
        unsigned long scrollLines = defaultScrollLinesPerWheelDelta;
        SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
        if (scrollLines == WHEEL_PAGESCROLL)
            result.scrollByPage = true;
        if (!result.scrollByPage)
            scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine;
    }

    // Set scroll amount based on above calculations.  WebKit expects positive
    // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
    if (horizontalScroll) {
        result.deltaX = scrollDelta;
        result.wheelTicksX = wheelDelta;
    } else {
        result.deltaY = scrollDelta;
        result.wheelTicksY = wheelDelta;
    }

    return result;
}

} // namespace WebKit
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>

#include "Document.h"
#include "Page.h"
#include "V8Binding.h"
#include "V8DOMWindow.h"
#include "V8Index.h"
#include "V8Proxy.h"
#undef LOG

#include "base/string_piece.h"
#include "grit/webkit_resources.h"
#include "webkit/glue/devtools/debugger_agent_impl.h"
#include "webkit/glue/devtools/debugger_agent_manager.h"
#include "webkit/glue/glue_util.h"
#include "webkit/glue/webdevtoolsagent_impl.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/webview_impl.h"

using WebCore::DOMWindow;
using WebCore::Document;
using WebCore::Frame;
using WebCore::Page;
using WebCore::String;
using WebCore::V8ClassIndex;
using WebCore::V8Custom;
using WebCore::V8DOMWindow;
using WebCore::V8Proxy;

DebuggerAgentImpl::DebuggerAgentImpl(
    WebViewImpl* web_view_impl,
    DebuggerAgentDelegate* delegate,
    WebDevToolsAgentImpl* webdevtools_agent)
    : web_view_impl_(web_view_impl),
      delegate_(delegate),
      webdevtools_agent_(webdevtools_agent),
      profiler_log_position_(0) {
  DebuggerAgentManager::DebugAttach(this);
}

DebuggerAgentImpl::~DebuggerAgentImpl() {
  DebuggerAgentManager::DebugDetach(this);
}

void DebuggerAgentImpl::DebugBreak() {
  DebuggerAgentManager::DebugBreak(this);
}

void DebuggerAgentImpl::GetContextId() {
  delegate_->SetContextId(webdevtools_agent_->host_id());
}

void DebuggerAgentImpl::StartProfiling() {
  v8::HandleScope scope;
  WebCore::V8Proxy* proxy = V8Proxy::retrieve(GetPage()->mainFrame());
  DCHECK(proxy && proxy->isContextInitialized());
  v8::Context::Scope context_scope(proxy->context());
  v8::V8::ResumeProfiler();
}

void DebuggerAgentImpl::StopProfiling() {
  v8::V8::PauseProfiler();
}

void DebuggerAgentImpl::IsProfilingStarted() {
  delegate_->DidIsProfilingStarted(!v8::V8::IsProfilerPaused());
}

void DebuggerAgentImpl::GetNextLogLines() {
  static char buffer[65536];
  int read_size = v8::V8::GetLogLines(
      profiler_log_position_, buffer, sizeof(buffer) - 1);
  profiler_log_position_ += read_size;
  buffer[read_size] = '\0';
  delegate_->DidGetNextLogLines(buffer);
}

void DebuggerAgentImpl::DebuggerOutput(const std::string& command) {
  delegate_->DebuggerOutput(command);
  webdevtools_agent_->ForceRepaint();
}

// static
void DebuggerAgentImpl::ResetUtilityContext(
    Document* document,
    v8::Persistent<v8::Context>* context) {
  if (!context->IsEmpty()) {
    context->Dispose();
    context->Clear();
  }
  v8::HandleScope scope;

  // TODO(pfeldman): Validate against Soeren.
  // Set up the DOM window as the prototype of the new global object.
  v8::Handle<v8::Context> window_context =
      V8Proxy::context(document->frame());
  v8::Handle<v8::Object> window_global = window_context->Global();
  v8::Handle<v8::Value> window_wrapper =
      V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, window_global);

  ASSERT(V8Proxy::convertDOMWrapperToNative<DOMWindow>(window_wrapper) ==
      document->frame()->domWindow());

  // Create a new environment using an empty template for the shadow
  // object.  Reuse the global object if one has been created earlier.
  v8::Handle<v8::ObjectTemplate> global_template =
      V8DOMWindow::GetShadowObjectTemplate();

  // Install a security handler with V8.
  global_template->SetAccessCheckCallbacks(
      V8Custom::v8DOMWindowNamedSecurityCheck,
      V8Custom::v8DOMWindowIndexedSecurityCheck,
      v8::Integer::New(V8ClassIndex::DOMWINDOW));

  *context = v8::Context::New(
      NULL /* no extensions */,
      global_template,
      v8::Handle<v8::Object>());
  v8::Context::Scope context_scope(*context);
  v8::Handle<v8::Object> global = (*context)->Global();

  v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__");
  global->Set(implicit_proto_string, window_wrapper);

  // Give the code running in the new context a way to get access to the
  // original context.
  global->Set(v8::String::New("contentWindow"), window_global);

  // Inject javascript into the context.
  StringPiece basejs = webkit_glue::GetDataResource(IDR_DEVTOOLS_BASE_JS);
  v8::Script::Compile(v8::String::New(basejs.as_string().c_str()))->Run();
  StringPiece injectjs = webkit_glue::GetDataResource(IDR_DEVTOOLS_INJECT_JS);
  v8::Script::Compile(v8::String::New(injectjs.as_string().c_str()))->Run();
  StringPiece inject_dispatchjs = webkit_glue::GetDataResource(
      IDR_DEVTOOLS_INJECT_DISPATCH_JS);
  v8::Script::Compile(v8::String::New(
      inject_dispatchjs.as_string().c_str()))->Run();
}

String DebuggerAgentImpl::ExecuteUtilityFunction(
    v8::Handle<v8::Context> context,
    const String &function_name,
    const String& json_args,
    String* exception) {
  v8::HandleScope scope;
  ASSERT(!context.IsEmpty());
  if (context.IsEmpty()) {
    *exception = "No window context.";
    return "";
  }
  v8::Context::Scope context_scope(context);
  v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(
      context->Global()->Get(v8::String::New("devtools$$dispatch")));

  v8::Handle<v8::String> function_name_wrapper = v8::Handle<v8::String>(
      v8::String::New(function_name.utf8().data()));
  v8::Handle<v8::String> json_args_wrapper = v8::Handle<v8::String>(
      v8::String::New(json_args.utf8().data()));
  v8::Handle<v8::Value> args[] = {
    function_name_wrapper,
    json_args_wrapper
  };

  v8::TryCatch try_catch;
  v8::Handle<v8::Value> res_obj = function->Call(context->Global(), 2, args);
  if (try_catch.HasCaught()) {
    *exception = WebCore::toWebCoreString(try_catch.Message()->Get());
    return "";
  } else {
    v8::Handle<v8::String> res_json = v8::Handle<v8::String>::Cast(res_obj);
    return WebCore::toWebCoreString(res_json);
  }
}

WebCore::Page* DebuggerAgentImpl::GetPage() {
  return web_view_impl_->page();
}
<commit_msg>Land Anton's patch to avoid throwing away type information on the window wrapper in the debugger agent.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>

#include "Document.h"
#include "Page.h"
#include "V8Binding.h"
#include "V8DOMWindow.h"
#include "V8Index.h"
#include "V8Proxy.h"
#undef LOG

#include "base/string_piece.h"
#include "grit/webkit_resources.h"
#include "webkit/glue/devtools/debugger_agent_impl.h"
#include "webkit/glue/devtools/debugger_agent_manager.h"
#include "webkit/glue/glue_util.h"
#include "webkit/glue/webdevtoolsagent_impl.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/webview_impl.h"

using WebCore::DOMWindow;
using WebCore::Document;
using WebCore::Frame;
using WebCore::Page;
using WebCore::String;
using WebCore::V8ClassIndex;
using WebCore::V8Custom;
using WebCore::V8DOMWindow;
using WebCore::V8Proxy;

DebuggerAgentImpl::DebuggerAgentImpl(
    WebViewImpl* web_view_impl,
    DebuggerAgentDelegate* delegate,
    WebDevToolsAgentImpl* webdevtools_agent)
    : web_view_impl_(web_view_impl),
      delegate_(delegate),
      webdevtools_agent_(webdevtools_agent),
      profiler_log_position_(0) {
  DebuggerAgentManager::DebugAttach(this);
}

DebuggerAgentImpl::~DebuggerAgentImpl() {
  DebuggerAgentManager::DebugDetach(this);
}

void DebuggerAgentImpl::DebugBreak() {
  DebuggerAgentManager::DebugBreak(this);
}

void DebuggerAgentImpl::GetContextId() {
  delegate_->SetContextId(webdevtools_agent_->host_id());
}

void DebuggerAgentImpl::StartProfiling() {
  v8::HandleScope scope;
  WebCore::V8Proxy* proxy = V8Proxy::retrieve(GetPage()->mainFrame());
  DCHECK(proxy && proxy->isContextInitialized());
  v8::Context::Scope context_scope(proxy->context());
  v8::V8::ResumeProfiler();
}

void DebuggerAgentImpl::StopProfiling() {
  v8::V8::PauseProfiler();
}

void DebuggerAgentImpl::IsProfilingStarted() {
  delegate_->DidIsProfilingStarted(!v8::V8::IsProfilerPaused());
}

void DebuggerAgentImpl::GetNextLogLines() {
  static char buffer[65536];
  int read_size = v8::V8::GetLogLines(
      profiler_log_position_, buffer, sizeof(buffer) - 1);
  profiler_log_position_ += read_size;
  buffer[read_size] = '\0';
  delegate_->DidGetNextLogLines(buffer);
}

void DebuggerAgentImpl::DebuggerOutput(const std::string& command) {
  delegate_->DebuggerOutput(command);
  webdevtools_agent_->ForceRepaint();
}

// static
void DebuggerAgentImpl::ResetUtilityContext(
    Document* document,
    v8::Persistent<v8::Context>* context) {
  if (!context->IsEmpty()) {
    context->Dispose();
    context->Clear();
  }
  v8::HandleScope scope;

  // TODO(pfeldman): Validate against Soeren.
  // Set up the DOM window as the prototype of the new global object.
  v8::Handle<v8::Context> window_context =
      V8Proxy::context(document->frame());
  v8::Handle<v8::Object> window_global = window_context->Global();
  v8::Handle<v8::Object> window_wrapper =
      V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, window_global);

  ASSERT(V8Proxy::convertDOMWrapperToNative<DOMWindow>(window_wrapper) ==
      document->frame()->domWindow());

  // Create a new environment using an empty template for the shadow
  // object.  Reuse the global object if one has been created earlier.
  v8::Handle<v8::ObjectTemplate> global_template =
      V8DOMWindow::GetShadowObjectTemplate();

  // Install a security handler with V8.
  global_template->SetAccessCheckCallbacks(
      V8Custom::v8DOMWindowNamedSecurityCheck,
      V8Custom::v8DOMWindowIndexedSecurityCheck,
      v8::Integer::New(V8ClassIndex::DOMWINDOW));

  *context = v8::Context::New(
      NULL /* no extensions */,
      global_template,
      v8::Handle<v8::Object>());
  v8::Context::Scope context_scope(*context);
  v8::Handle<v8::Object> global = (*context)->Global();

  v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__");
  global->Set(implicit_proto_string, window_wrapper);

  // Give the code running in the new context a way to get access to the
  // original context.
  global->Set(v8::String::New("contentWindow"), window_global);

  // Inject javascript into the context.
  StringPiece basejs = webkit_glue::GetDataResource(IDR_DEVTOOLS_BASE_JS);
  v8::Script::Compile(v8::String::New(basejs.as_string().c_str()))->Run();
  StringPiece injectjs = webkit_glue::GetDataResource(IDR_DEVTOOLS_INJECT_JS);
  v8::Script::Compile(v8::String::New(injectjs.as_string().c_str()))->Run();
  StringPiece inject_dispatchjs = webkit_glue::GetDataResource(
      IDR_DEVTOOLS_INJECT_DISPATCH_JS);
  v8::Script::Compile(v8::String::New(
      inject_dispatchjs.as_string().c_str()))->Run();
}

String DebuggerAgentImpl::ExecuteUtilityFunction(
    v8::Handle<v8::Context> context,
    const String &function_name,
    const String& json_args,
    String* exception) {
  v8::HandleScope scope;
  ASSERT(!context.IsEmpty());
  if (context.IsEmpty()) {
    *exception = "No window context.";
    return "";
  }
  v8::Context::Scope context_scope(context);
  v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(
      context->Global()->Get(v8::String::New("devtools$$dispatch")));

  v8::Handle<v8::String> function_name_wrapper = v8::Handle<v8::String>(
      v8::String::New(function_name.utf8().data()));
  v8::Handle<v8::String> json_args_wrapper = v8::Handle<v8::String>(
      v8::String::New(json_args.utf8().data()));
  v8::Handle<v8::Value> args[] = {
    function_name_wrapper,
    json_args_wrapper
  };

  v8::TryCatch try_catch;
  v8::Handle<v8::Value> res_obj = function->Call(context->Global(), 2, args);
  if (try_catch.HasCaught()) {
    *exception = WebCore::toWebCoreString(try_catch.Message()->Get());
    return "";
  } else {
    v8::Handle<v8::String> res_json = v8::Handle<v8::String>::Cast(res_obj);
    return WebCore::toWebCoreString(res_json);
  }
}

WebCore::Page* DebuggerAgentImpl::GetPage() {
  return web_view_impl_->page();
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "Color.h"

#include "SkColor.h"

#include <wtf/Assertions.h>

namespace WebCore {

COMPILE_ASSERT(SK_ColorBLACK == Color::black, SkColorAndColorAreLaidOutTheSame);

Color WebCore::focusRingColor()
{
    static Color focusRingColor(229, 151, 0, 255);
    return focusRingColor;
}

} // namespace WebCore
<commit_msg>Fix at least the Linux build.  Boo MSVC for not complaining about this.<commit_after>/*
 * Copyright (C) 2008 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "Color.h"

#include "SkColor.h"

#include <wtf/Assertions.h>

namespace WebCore {

COMPILE_ASSERT(SK_ColorBLACK == Color::black, SkColorAndColorAreLaidOutTheSame);

Color focusRingColor()
{
    static Color focusRingColor(229, 151, 0, 255);
    return focusRingColor;
}

} // namespace WebCore
<|endoftext|>


<commit_before>//===- unittests/Basic/FileMangerTest.cpp ------------ FileManger tests ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/FileSystemStatCache.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "gtest/gtest.h"

using namespace llvm;
using namespace clang;

namespace {

// Used to create a fake file system for running the tests with such
// that the tests are not affected by the structure/contents of the
// file system on the machine running the tests.
class FakeStatCache : public FileSystemStatCache {
private:
  // Maps a file/directory path to its desired stat result.  Anything
  // not in this map is considered to not exist in the file system.
  llvm::StringMap<llvm::vfs::Status, llvm::BumpPtrAllocator> StatCalls;

  void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) {
#ifndef _WIN32
    SmallString<128> NormalizedPath(Path);
    llvm::sys::path::native(NormalizedPath);
    Path = NormalizedPath.c_str();
#endif

    auto fileType = IsFile ?
      llvm::sys::fs::file_type::regular_file :
      llvm::sys::fs::file_type::directory_file;
    llvm::vfs::Status Status(Path, llvm::sys::fs::UniqueID(1, INode),
                             /*MTime*/{}, /*User*/0, /*Group*/0,
                             /*Size*/0, fileType,
                             llvm::sys::fs::perms::all_all);
    StatCalls[Path] = Status;
  }

public:
  // Inject a file with the given inode value to the fake file system.
  void InjectFile(const char *Path, ino_t INode) {
    InjectFileOrDirectory(Path, INode, /*IsFile=*/true);
  }

  // Inject a directory with the given inode value to the fake file system.
  void InjectDirectory(const char *Path, ino_t INode) {
    InjectFileOrDirectory(Path, INode, /*IsFile=*/false);
  }

  // Implement FileSystemStatCache::getStat().
  std::error_code getStat(StringRef Path, llvm::vfs::Status &Status,
                          bool isFile,
                          std::unique_ptr<llvm::vfs::File> *F,
                          llvm::vfs::FileSystem &FS) override {
#ifndef _WIN32
    SmallString<128> NormalizedPath(Path);
    llvm::sys::path::native(NormalizedPath);
    Path = NormalizedPath.c_str();
#endif

    if (StatCalls.count(Path) != 0) {
      Status = StatCalls[Path];
      return std::error_code();
    }

    return std::make_error_code(std::errc::no_such_file_or_directory);
  }
};

// The test fixture.
class FileManagerTest : public ::testing::Test {
 protected:
  FileManagerTest() : manager(options) {
  }

  FileSystemOptions options;
  FileManager manager;
};

// When a virtual file is added, its getDir() field is set correctly
// (not NULL, correct name).
TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) {
  const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0);
  ASSERT_TRUE(file != nullptr);

  const DirectoryEntry *dir = file->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ(".", dir->getName());

  file = manager.getVirtualFile("x/y/z.cpp", 42, 0);
  ASSERT_TRUE(file != nullptr);

  dir = file->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ("x/y", dir->getName());
}

// Before any virtual file is added, no virtual directory exists.
TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
  // An empty FakeStatCache causes all stat calls made by the
  // FileManager to report "file/directory doesn't exist".  This
  // avoids the possibility of the result of this test being affected
  // by what's in the real file system.
  manager.setStatCache(llvm::make_unique<FakeStatCache>());

  ASSERT_FALSE(manager.getDirectory("virtual/dir/foo"));
  ASSERT_FALSE(manager.getDirectory("virtual/dir"));
  ASSERT_FALSE(manager.getDirectory("virtual"));
}

// When a virtual file is added, all of its ancestors should be created.
TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
  // Fake an empty real file system.
  manager.setStatCache(llvm::make_unique<FakeStatCache>());

  manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
  ASSERT_FALSE(manager.getDirectory("virtual/dir/foo"));

  auto dir = manager.getDirectory("virtual/dir");
  ASSERT_TRUE(dir);
  EXPECT_EQ("virtual/dir", (*dir)->getName());

  dir = manager.getDirectory("virtual");
  ASSERT_TRUE(dir);
  EXPECT_EQ("virtual", (*dir)->getName());
}

// getFile() returns non-NULL if a real file exists at the given path.
TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("/tmp", 42);
  statCache->InjectFile("/tmp/test", 43);

#ifdef _WIN32
  const char *DirName = "C:.";
  const char *FileName = "C:test";
  statCache->InjectDirectory(DirName, 44);
  statCache->InjectFile(FileName, 45);
#endif

  manager.setStatCache(std::move(statCache));

  auto file = manager.getFile("/tmp/test");
  ASSERT_TRUE(file);
  ASSERT_TRUE((*file)->isValid());
  EXPECT_EQ("/tmp/test", (*file)->getName());

  const DirectoryEntry *dir = (*file)->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ("/tmp", dir->getName());

#ifdef _WIN32
  file = manager.getFile(FileName);
  ASSERT_TRUE(file);

  dir = file->getDir();
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ(DirName, dir->getName());
#endif
}

// getFile() returns non-NULL if a virtual file exists at the given path.
TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
  // Fake an empty real file system.
  manager.setStatCache(llvm::make_unique<FakeStatCache>());

  manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
  auto file = manager.getFile("virtual/dir/bar.h");
  ASSERT_TRUE(file);
  ASSERT_TRUE((*file)->isValid());
  EXPECT_EQ("virtual/dir/bar.h", (*file)->getName());

  const DirectoryEntry *dir = (*file)->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ("virtual/dir", dir->getName());
}

// getFile() returns different FileEntries for different paths when
// there's no aliasing.
TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
  // Inject two fake files into the file system.  Different inodes
  // mean the files are not symlinked together.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory(".", 41);
  statCache->InjectFile("foo.cpp", 42);
  statCache->InjectFile("bar.cpp", 43);
  manager.setStatCache(std::move(statCache));

  auto fileFoo = manager.getFile("foo.cpp");
  auto fileBar = manager.getFile("bar.cpp");
  ASSERT_TRUE(fileFoo);
  ASSERT_TRUE((*fileFoo)->isValid());
  ASSERT_TRUE(fileBar);
  ASSERT_TRUE((*fileBar)->isValid());
  EXPECT_NE(*fileFoo, *fileBar);
}

// getFile() returns an error if neither a real file nor a virtual file
// exists at the given path.
TEST_F(FileManagerTest, getFileReturnsErrorForNonexistentFile) {
  // Inject a fake foo.cpp into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory(".", 41);
  statCache->InjectFile("foo.cpp", 42);
  statCache->InjectDirectory("MyDirectory", 49);
  manager.setStatCache(std::move(statCache));

  // Create a virtual bar.cpp file.
  manager.getVirtualFile("bar.cpp", 200, 0);

  auto file = manager.getFile("xyz.txt");
  ASSERT_FALSE(file);
  ASSERT_EQ(file.getError(), std::errc::no_such_file_or_directory);

  auto readingDirAsFile = manager.getFile("MyDirectory");
  ASSERT_FALSE(readingDirAsFile);
  ASSERT_EQ(readingDirAsFile.getError(), std::errc::is_a_directory);

  auto readingFileAsDir = manager.getDirectory("foo.cpp");
  ASSERT_FALSE(readingFileAsDir);
  ASSERT_EQ(readingFileAsDir.getError(), std::errc::not_a_directory);
}

// The following tests apply to Unix-like system only.

#ifndef _WIN32

// getFile() returns the same FileEntry for real files that are aliases.
TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
  // Inject two real files with the same inode.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("abc", 41);
  statCache->InjectFile("abc/foo.cpp", 42);
  statCache->InjectFile("abc/bar.cpp", 42);
  manager.setStatCache(std::move(statCache));

  auto f1 = manager.getFile("abc/foo.cpp");
  auto f2 = manager.getFile("abc/bar.cpp");

  EXPECT_EQ(f1 ? *f1 : nullptr,
            f2 ? *f2 : nullptr);
}

// getFile() returns the same FileEntry for virtual files that have
// corresponding real files that are aliases.
TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
  // Inject two real files with the same inode.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("abc", 41);
  statCache->InjectFile("abc/foo.cpp", 42);
  statCache->InjectFile("abc/bar.cpp", 42);
  manager.setStatCache(std::move(statCache));

  ASSERT_TRUE(manager.getVirtualFile("abc/foo.cpp", 100, 0)->isValid());
  ASSERT_TRUE(manager.getVirtualFile("abc/bar.cpp", 200, 0)->isValid());

  auto f1 = manager.getFile("abc/foo.cpp");
  auto f2 = manager.getFile("abc/bar.cpp");

  EXPECT_EQ(f1 ? *f1 : nullptr,
            f2 ? *f2 : nullptr);
}

// getFile() Should return the same entry as getVirtualFile if the file actually
// is a virtual file, even if the name is not exactly the same (but is after
// normalisation done by the file system, like on Windows). This can be checked
// here by checking the size.
TEST_F(FileManagerTest, getVirtualFileWithDifferentName) {
  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("c:\\tmp", 42);
  statCache->InjectFile("c:\\tmp\\test", 43);

  manager.setStatCache(std::move(statCache));

  // Inject the virtual file:
  const FileEntry *file1 = manager.getVirtualFile("c:\\tmp\\test", 123, 1);
  ASSERT_TRUE(file1 != nullptr);
  ASSERT_TRUE(file1->isValid());
  EXPECT_EQ(43U, file1->getUniqueID().getFile());
  EXPECT_EQ(123, file1->getSize());

  // Lookup the virtual file with a different name:
  auto file2 = manager.getFile("c:/tmp/test", 100, 1);
  ASSERT_TRUE(file2);
  ASSERT_TRUE((*file2)->isValid());
  // Check that it's the same UFE:
  EXPECT_EQ(file1, *file2);
  EXPECT_EQ(43U, (*file2)->getUniqueID().getFile());
  // Check that the contents of the UFE are not overwritten by the entry in the
  // filesystem:
  EXPECT_EQ(123, (*file2)->getSize());
}

#endif  // !_WIN32

TEST_F(FileManagerTest, makeAbsoluteUsesVFS) {
  SmallString<64> CustomWorkingDir;
#ifdef _WIN32
  CustomWorkingDir = "C:";
#else
  CustomWorkingDir = "/";
#endif
  llvm::sys::path::append(CustomWorkingDir, "some", "weird", "path");

  auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
      new llvm::vfs::InMemoryFileSystem);
  // setCurrentworkingdirectory must finish without error.
  ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));

  FileSystemOptions Opts;
  FileManager Manager(Opts, FS);

  SmallString<64> Path("a/foo.cpp");

  SmallString<64> ExpectedResult(CustomWorkingDir);
  llvm::sys::path::append(ExpectedResult, Path);

  ASSERT_TRUE(Manager.makeAbsolutePath(Path));
  EXPECT_EQ(Path, ExpectedResult);
}

// getVirtualFile should always fill the real path.
TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) {
  SmallString<64> CustomWorkingDir;
#ifdef _WIN32
  CustomWorkingDir = "C:/";
#else
  CustomWorkingDir = "/";
#endif

  auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
      new llvm::vfs::InMemoryFileSystem);
  // setCurrentworkingdirectory must finish without error.
  ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));

  FileSystemOptions Opts;
  FileManager Manager(Opts, FS);

  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("/tmp", 42);
  statCache->InjectFile("/tmp/test", 43);

  Manager.setStatCache(std::move(statCache));

  // Check for real path.
  const FileEntry *file = Manager.getVirtualFile("/tmp/test", 123, 1);
  ASSERT_TRUE(file != nullptr);
  ASSERT_TRUE(file->isValid());
  SmallString<64> ExpectedResult = CustomWorkingDir;

  llvm::sys::path::append(ExpectedResult, "tmp", "test");
  EXPECT_EQ(file->tryGetRealPathName(), ExpectedResult);
}

TEST_F(FileManagerTest, getFileDontOpenRealPath) {
  SmallString<64> CustomWorkingDir;
#ifdef _WIN32
  CustomWorkingDir = "C:/";
#else
  CustomWorkingDir = "/";
#endif

  auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
      new llvm::vfs::InMemoryFileSystem);
  // setCurrentworkingdirectory must finish without error.
  ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));

  FileSystemOptions Opts;
  FileManager Manager(Opts, FS);

  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("/tmp", 42);
  statCache->InjectFile("/tmp/test", 43);

  Manager.setStatCache(std::move(statCache));

  // Check for real path.
  auto file = Manager.getFile("/tmp/test", /*OpenFile=*/false);
  ASSERT_TRUE(file);
  ASSERT_TRUE((*file)->isValid());
  SmallString<64> ExpectedResult = CustomWorkingDir;

  llvm::sys::path::append(ExpectedResult, "tmp", "test");
  EXPECT_EQ((*file)->tryGetRealPathName(), ExpectedResult);
}

} // anonymous namespace
<commit_msg>Fix Windows branch of FileManagerTest changes<commit_after>//===- unittests/Basic/FileMangerTest.cpp ------------ FileManger tests ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/FileSystemStatCache.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "gtest/gtest.h"

using namespace llvm;
using namespace clang;

namespace {

// Used to create a fake file system for running the tests with such
// that the tests are not affected by the structure/contents of the
// file system on the machine running the tests.
class FakeStatCache : public FileSystemStatCache {
private:
  // Maps a file/directory path to its desired stat result.  Anything
  // not in this map is considered to not exist in the file system.
  llvm::StringMap<llvm::vfs::Status, llvm::BumpPtrAllocator> StatCalls;

  void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) {
#ifndef _WIN32
    SmallString<128> NormalizedPath(Path);
    llvm::sys::path::native(NormalizedPath);
    Path = NormalizedPath.c_str();
#endif

    auto fileType = IsFile ?
      llvm::sys::fs::file_type::regular_file :
      llvm::sys::fs::file_type::directory_file;
    llvm::vfs::Status Status(Path, llvm::sys::fs::UniqueID(1, INode),
                             /*MTime*/{}, /*User*/0, /*Group*/0,
                             /*Size*/0, fileType,
                             llvm::sys::fs::perms::all_all);
    StatCalls[Path] = Status;
  }

public:
  // Inject a file with the given inode value to the fake file system.
  void InjectFile(const char *Path, ino_t INode) {
    InjectFileOrDirectory(Path, INode, /*IsFile=*/true);
  }

  // Inject a directory with the given inode value to the fake file system.
  void InjectDirectory(const char *Path, ino_t INode) {
    InjectFileOrDirectory(Path, INode, /*IsFile=*/false);
  }

  // Implement FileSystemStatCache::getStat().
  std::error_code getStat(StringRef Path, llvm::vfs::Status &Status,
                          bool isFile,
                          std::unique_ptr<llvm::vfs::File> *F,
                          llvm::vfs::FileSystem &FS) override {
#ifndef _WIN32
    SmallString<128> NormalizedPath(Path);
    llvm::sys::path::native(NormalizedPath);
    Path = NormalizedPath.c_str();
#endif

    if (StatCalls.count(Path) != 0) {
      Status = StatCalls[Path];
      return std::error_code();
    }

    return std::make_error_code(std::errc::no_such_file_or_directory);
  }
};

// The test fixture.
class FileManagerTest : public ::testing::Test {
 protected:
  FileManagerTest() : manager(options) {
  }

  FileSystemOptions options;
  FileManager manager;
};

// When a virtual file is added, its getDir() field is set correctly
// (not NULL, correct name).
TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) {
  const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0);
  ASSERT_TRUE(file != nullptr);

  const DirectoryEntry *dir = file->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ(".", dir->getName());

  file = manager.getVirtualFile("x/y/z.cpp", 42, 0);
  ASSERT_TRUE(file != nullptr);

  dir = file->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ("x/y", dir->getName());
}

// Before any virtual file is added, no virtual directory exists.
TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
  // An empty FakeStatCache causes all stat calls made by the
  // FileManager to report "file/directory doesn't exist".  This
  // avoids the possibility of the result of this test being affected
  // by what's in the real file system.
  manager.setStatCache(llvm::make_unique<FakeStatCache>());

  ASSERT_FALSE(manager.getDirectory("virtual/dir/foo"));
  ASSERT_FALSE(manager.getDirectory("virtual/dir"));
  ASSERT_FALSE(manager.getDirectory("virtual"));
}

// When a virtual file is added, all of its ancestors should be created.
TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
  // Fake an empty real file system.
  manager.setStatCache(llvm::make_unique<FakeStatCache>());

  manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
  ASSERT_FALSE(manager.getDirectory("virtual/dir/foo"));

  auto dir = manager.getDirectory("virtual/dir");
  ASSERT_TRUE(dir);
  EXPECT_EQ("virtual/dir", (*dir)->getName());

  dir = manager.getDirectory("virtual");
  ASSERT_TRUE(dir);
  EXPECT_EQ("virtual", (*dir)->getName());
}

// getFile() returns non-NULL if a real file exists at the given path.
TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("/tmp", 42);
  statCache->InjectFile("/tmp/test", 43);

#ifdef _WIN32
  const char *DirName = "C:.";
  const char *FileName = "C:test";
  statCache->InjectDirectory(DirName, 44);
  statCache->InjectFile(FileName, 45);
#endif

  manager.setStatCache(std::move(statCache));

  auto file = manager.getFile("/tmp/test");
  ASSERT_TRUE(file);
  ASSERT_TRUE((*file)->isValid());
  EXPECT_EQ("/tmp/test", (*file)->getName());

  const DirectoryEntry *dir = (*file)->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ("/tmp", dir->getName());

#ifdef _WIN32
  file = manager.getFile(FileName);
  ASSERT_TRUE(file);

  dir = (*file)->getDir();
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ(DirName, dir->getName());
#endif
}

// getFile() returns non-NULL if a virtual file exists at the given path.
TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
  // Fake an empty real file system.
  manager.setStatCache(llvm::make_unique<FakeStatCache>());

  manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
  auto file = manager.getFile("virtual/dir/bar.h");
  ASSERT_TRUE(file);
  ASSERT_TRUE((*file)->isValid());
  EXPECT_EQ("virtual/dir/bar.h", (*file)->getName());

  const DirectoryEntry *dir = (*file)->getDir();
  ASSERT_TRUE(dir != nullptr);
  EXPECT_EQ("virtual/dir", dir->getName());
}

// getFile() returns different FileEntries for different paths when
// there's no aliasing.
TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
  // Inject two fake files into the file system.  Different inodes
  // mean the files are not symlinked together.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory(".", 41);
  statCache->InjectFile("foo.cpp", 42);
  statCache->InjectFile("bar.cpp", 43);
  manager.setStatCache(std::move(statCache));

  auto fileFoo = manager.getFile("foo.cpp");
  auto fileBar = manager.getFile("bar.cpp");
  ASSERT_TRUE(fileFoo);
  ASSERT_TRUE((*fileFoo)->isValid());
  ASSERT_TRUE(fileBar);
  ASSERT_TRUE((*fileBar)->isValid());
  EXPECT_NE(*fileFoo, *fileBar);
}

// getFile() returns an error if neither a real file nor a virtual file
// exists at the given path.
TEST_F(FileManagerTest, getFileReturnsErrorForNonexistentFile) {
  // Inject a fake foo.cpp into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory(".", 41);
  statCache->InjectFile("foo.cpp", 42);
  statCache->InjectDirectory("MyDirectory", 49);
  manager.setStatCache(std::move(statCache));

  // Create a virtual bar.cpp file.
  manager.getVirtualFile("bar.cpp", 200, 0);

  auto file = manager.getFile("xyz.txt");
  ASSERT_FALSE(file);
  ASSERT_EQ(file.getError(), std::errc::no_such_file_or_directory);

  auto readingDirAsFile = manager.getFile("MyDirectory");
  ASSERT_FALSE(readingDirAsFile);
  ASSERT_EQ(readingDirAsFile.getError(), std::errc::is_a_directory);

  auto readingFileAsDir = manager.getDirectory("foo.cpp");
  ASSERT_FALSE(readingFileAsDir);
  ASSERT_EQ(readingFileAsDir.getError(), std::errc::not_a_directory);
}

// The following tests apply to Unix-like system only.

#ifndef _WIN32

// getFile() returns the same FileEntry for real files that are aliases.
TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
  // Inject two real files with the same inode.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("abc", 41);
  statCache->InjectFile("abc/foo.cpp", 42);
  statCache->InjectFile("abc/bar.cpp", 42);
  manager.setStatCache(std::move(statCache));

  auto f1 = manager.getFile("abc/foo.cpp");
  auto f2 = manager.getFile("abc/bar.cpp");

  EXPECT_EQ(f1 ? *f1 : nullptr,
            f2 ? *f2 : nullptr);
}

// getFile() returns the same FileEntry for virtual files that have
// corresponding real files that are aliases.
TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
  // Inject two real files with the same inode.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("abc", 41);
  statCache->InjectFile("abc/foo.cpp", 42);
  statCache->InjectFile("abc/bar.cpp", 42);
  manager.setStatCache(std::move(statCache));

  ASSERT_TRUE(manager.getVirtualFile("abc/foo.cpp", 100, 0)->isValid());
  ASSERT_TRUE(manager.getVirtualFile("abc/bar.cpp", 200, 0)->isValid());

  auto f1 = manager.getFile("abc/foo.cpp");
  auto f2 = manager.getFile("abc/bar.cpp");

  EXPECT_EQ(f1 ? *f1 : nullptr,
            f2 ? *f2 : nullptr);
}

// getFile() Should return the same entry as getVirtualFile if the file actually
// is a virtual file, even if the name is not exactly the same (but is after
// normalisation done by the file system, like on Windows). This can be checked
// here by checking the size.
TEST_F(FileManagerTest, getVirtualFileWithDifferentName) {
  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("c:\\tmp", 42);
  statCache->InjectFile("c:\\tmp\\test", 43);

  manager.setStatCache(std::move(statCache));

  // Inject the virtual file:
  const FileEntry *file1 = manager.getVirtualFile("c:\\tmp\\test", 123, 1);
  ASSERT_TRUE(file1 != nullptr);
  ASSERT_TRUE(file1->isValid());
  EXPECT_EQ(43U, file1->getUniqueID().getFile());
  EXPECT_EQ(123, file1->getSize());

  // Lookup the virtual file with a different name:
  auto file2 = manager.getFile("c:/tmp/test", 100, 1);
  ASSERT_TRUE(file2);
  ASSERT_TRUE((*file2)->isValid());
  // Check that it's the same UFE:
  EXPECT_EQ(file1, *file2);
  EXPECT_EQ(43U, (*file2)->getUniqueID().getFile());
  // Check that the contents of the UFE are not overwritten by the entry in the
  // filesystem:
  EXPECT_EQ(123, (*file2)->getSize());
}

#endif  // !_WIN32

TEST_F(FileManagerTest, makeAbsoluteUsesVFS) {
  SmallString<64> CustomWorkingDir;
#ifdef _WIN32
  CustomWorkingDir = "C:";
#else
  CustomWorkingDir = "/";
#endif
  llvm::sys::path::append(CustomWorkingDir, "some", "weird", "path");

  auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
      new llvm::vfs::InMemoryFileSystem);
  // setCurrentworkingdirectory must finish without error.
  ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));

  FileSystemOptions Opts;
  FileManager Manager(Opts, FS);

  SmallString<64> Path("a/foo.cpp");

  SmallString<64> ExpectedResult(CustomWorkingDir);
  llvm::sys::path::append(ExpectedResult, Path);

  ASSERT_TRUE(Manager.makeAbsolutePath(Path));
  EXPECT_EQ(Path, ExpectedResult);
}

// getVirtualFile should always fill the real path.
TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) {
  SmallString<64> CustomWorkingDir;
#ifdef _WIN32
  CustomWorkingDir = "C:/";
#else
  CustomWorkingDir = "/";
#endif

  auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
      new llvm::vfs::InMemoryFileSystem);
  // setCurrentworkingdirectory must finish without error.
  ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));

  FileSystemOptions Opts;
  FileManager Manager(Opts, FS);

  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("/tmp", 42);
  statCache->InjectFile("/tmp/test", 43);

  Manager.setStatCache(std::move(statCache));

  // Check for real path.
  const FileEntry *file = Manager.getVirtualFile("/tmp/test", 123, 1);
  ASSERT_TRUE(file != nullptr);
  ASSERT_TRUE(file->isValid());
  SmallString<64> ExpectedResult = CustomWorkingDir;

  llvm::sys::path::append(ExpectedResult, "tmp", "test");
  EXPECT_EQ(file->tryGetRealPathName(), ExpectedResult);
}

TEST_F(FileManagerTest, getFileDontOpenRealPath) {
  SmallString<64> CustomWorkingDir;
#ifdef _WIN32
  CustomWorkingDir = "C:/";
#else
  CustomWorkingDir = "/";
#endif

  auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
      new llvm::vfs::InMemoryFileSystem);
  // setCurrentworkingdirectory must finish without error.
  ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));

  FileSystemOptions Opts;
  FileManager Manager(Opts, FS);

  // Inject fake files into the file system.
  auto statCache = llvm::make_unique<FakeStatCache>();
  statCache->InjectDirectory("/tmp", 42);
  statCache->InjectFile("/tmp/test", 43);

  Manager.setStatCache(std::move(statCache));

  // Check for real path.
  auto file = Manager.getFile("/tmp/test", /*OpenFile=*/false);
  ASSERT_TRUE(file);
  ASSERT_TRUE((*file)->isValid());
  SmallString<64> ExpectedResult = CustomWorkingDir;

  llvm::sys::path::append(ExpectedResult, "tmp", "test");
  EXPECT_EQ((*file)->tryGetRealPathName(), ExpectedResult);
}

} // anonymous namespace
<|endoftext|>


<commit_before>#include "index/MeshStream.hpp"

#include <cstdint>

using namespace utymap::index;
using namespace utymap::math;

namespace {
const float Precision = 1E7;

template<typename T>
T read(std::istream &stream) {
  T data;
  stream.read(reinterpret_cast<char *>(&data), sizeof(data));
  return data;
}

/// Restores double stored as signed 4-bytes integer.
template<>
double read<double>(std::istream &stream) {
  std::int32_t data;
  stream.read(reinterpret_cast<char *>(&data), sizeof(data));
  return data / Precision;
}

template<typename T>
void write(std::ostream &stream, const T &data) {
  stream.write(reinterpret_cast<const char *>(&data), sizeof(data));
}

/// Stores double as signed 4-bytes integer.
///
template<>
void write<double>(std::ostream &stream, const double &data) {
  auto simplified = static_cast<std::int32_t>(data * Precision);
  stream.write(reinterpret_cast<const char *>(&simplified), sizeof(simplified));
}

template<typename T>
std::ostream &operator<<(std::ostream &stream, const std::vector<T> &data) {
  auto size = static_cast<std::uint32_t>(data.size());
  stream.write(reinterpret_cast<const char *>(&size), sizeof(size));
  for (const auto &item : data)
    write<T>(stream, item);
  return stream;
}

template<typename T>
std::istream &operator>>(std::istream &stream, std::vector<T> &data) {
  std::uint32_t size = 0;
  stream.read(reinterpret_cast<char *>(&size), sizeof(size));
  data.resize(size);
  for (size_t i = 0; i < size; ++i)
    data[i] = read<T>(stream);
  return stream;
}

std::ostream &operator<<(std::ostream &stream, const Mesh &mesh) {
  return stream << mesh.name.c_str() << '\0' << mesh.vertices << mesh.triangles
                << mesh.colors << mesh.uvs << mesh.uvMap;
}

std::istream &operator>>(std::istream &stream, Mesh &mesh) {
  std::getline(stream, mesh.name, '\0');
  return stream >> mesh.vertices >> mesh.triangles
                >> mesh.colors >> mesh.uvs >> mesh.uvMap;
}
}

Mesh MeshStream::read(std::istream &stream) {
  Mesh mesh("");
  stream >> mesh;
  return std::move(mesh);
}

void MeshStream::write(std::ostream &stream, const Mesh &mesh) {
  stream << mesh;
}
<commit_msg>core: fix elevation overflow problem in mesh caching<commit_after>#include "index/MeshStream.hpp"

#include <cstdint>

using namespace utymap::index;
using namespace utymap::math;

namespace {
const double Precision = 1E7;

template<typename T>
void initData(std::istream &stream, std::vector<T> &data) {
  std::uint32_t size = 0;
  stream.read(reinterpret_cast<char *>(&size), sizeof(size));
  data.resize(size);
}

template<typename T>
void initData(std::ostream &stream, const std::vector<T> &data) {
  auto size = static_cast<std::uint32_t>(data.size());
  stream.write(reinterpret_cast<const char *>(&size), sizeof(size));
}

template<typename T>
T read(std::istream &stream) {
  T data;
  stream.read(reinterpret_cast<char *>(&data), sizeof(data));
  return data;
}

/// Restores double stored as signed 4-bytes integer.
template<>
double read<double>(std::istream &stream) {
  std::int32_t data;
  stream.read(reinterpret_cast<char *>(&data), sizeof(data));
  return data / Precision;
}

template<typename T>
void write(std::ostream &stream, const T &data) {
  stream.write(reinterpret_cast<const char *>(&data), sizeof(data));
}

/// Stores double as signed 4-bytes integer.
///
template<>
void write<double>(std::ostream &stream, const double &data) {
  auto simplified = static_cast<std::int32_t>(data * Precision);
  stream.write(reinterpret_cast<const char *>(&simplified), sizeof(simplified));
}

template<typename T>
std::ostream &operator<<(std::ostream &stream, const std::vector<T> &data) {
  initData(stream, data);
  for (const auto &item : data)
    write<T>(stream, item);
  return stream;
}

template<typename T>
std::istream &operator>>(std::istream &stream, std::vector<T> &data) {
  initData(stream, data);
  for (size_t i = 0; i < data.size(); ++i)
    data[i] = read<T>(stream);
  return stream;
}

void readVertices(std::istream &stream, std::vector<double> &data) {
  initData(stream, data);
  for (size_t i = 0; i < data.size(); ++i)
    // NOTE store elevation as float, not as packed double
    data[i] = (i + 1) % 3 == 0 ? read<float>(stream) : read<double>(stream);
}

void writeVertices(std::ostream &stream, const std::vector<double> &data) {
  initData(stream, data);
  for (size_t i = 0; i < data.size(); ++i) {
    if ((i + 1) % 3 == 0)
      write<float>(stream, static_cast<float>(data[i]));
    else
      write<double>(stream, data[i]);
  }
}

std::ostream &operator<<(std::ostream &stream, const Mesh &mesh) {
  stream << mesh.name.c_str() << '\0';
  writeVertices(stream, mesh.vertices);
  return stream << mesh.triangles << mesh.colors << mesh.uvs << mesh.uvMap;
}

std::istream &operator>>(std::istream &stream, Mesh &mesh) {
  std::getline(stream, mesh.name, '\0');
  readVertices(stream, mesh.vertices);
  return stream >> mesh.triangles >> mesh.colors >> mesh.uvs >> mesh.uvMap;
}
}

Mesh MeshStream::read(std::istream &stream) {
  Mesh mesh("");
  stream >> mesh;
  return std::move(mesh);
}

void MeshStream::write(std::ostream &stream, const Mesh &mesh) {
  stream << mesh;
}
<|endoftext|>