Differential D3030 Diff 15156 ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h
Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h
/* Copyright (C) 2020 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "lib/self_test.h" | #include "lib/self_test.h" | ||||
#include "graphics/PreprocessorWrapper.h" | #include "graphics/PreprocessorWrapper.h" | ||||
#include "ps/CStr.h" | #include "ps/CStr.h" | ||||
#include "third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h" | #include "third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h" | ||||
#include <cctype> | |||||
#include <sstream> | |||||
class TestPreprocessor : public CxxTest::TestSuite | class TestPreprocessor : public CxxTest::TestSuite | ||||
{ | { | ||||
public: | public: | ||||
void setUp() | void setUp() | ||||
{ | { | ||||
Ogre::CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; | Ogre::CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; | ||||
} | } | ||||
struct PreprocessorResult | struct PreprocessorResult | ||||
{ | { | ||||
CStr8 output; | CStr8 output; | ||||
CStr8 loggerOutput; | CStr8 loggerOutput; | ||||
}; | }; | ||||
// Replaces consecutive spaces/tabs by single space/tab. | |||||
CStr CompressWhiteSpaces(const CStr& source) | |||||
{ | |||||
CStr result; | |||||
for (char ch : source) | |||||
if (!std::isblank(ch) || (result.empty() || result.back() != ch)) | |||||
result += ch; | |||||
return result; | |||||
} | |||||
PreprocessorResult Parse(const char* in) | PreprocessorResult Parse(const char* in) | ||||
{ | { | ||||
PreprocessorResult result; | PreprocessorResult result; | ||||
TestLogger logger; | TestLogger logger; | ||||
Ogre::CPreprocessor preproc; | Ogre::CPreprocessor preproc; | ||||
size_t len = 0; | size_t len = 0; | ||||
char* out = preproc.Parse(in, strlen(in), len); | char* out = preproc.Parse(in, strlen(in), len); | ||||
result.output = std::string(out, len); | result.output = std::string(out, len); | ||||
result.loggerOutput = logger.GetOutput(); | result.loggerOutput = logger.GetOutput(); | ||||
// Free output if it's not inside the source string | // Free output if it's not inside the source string | ||||
if (!(out >= in && out < in + strlen(in))) | if (!(out >= in && out < in + strlen(in))) | ||||
free(out); | free(out); | ||||
return result; | return result; | ||||
} | } | ||||
PreprocessorResult ParseWithIncludes(const char* in, CPreprocessorWrapper::IncludeRetrieverCallback includeCallback) | |||||
{ | |||||
PreprocessorResult result; | |||||
TestLogger logger; | |||||
CPreprocessorWrapper preprocessor(includeCallback); | |||||
result.output = preprocessor.Preprocess(in); | |||||
result.loggerOutput = logger.GetOutput(); | |||||
return result; | |||||
} | |||||
void test_basic() | void test_basic() | ||||
{ | { | ||||
PreprocessorResult result = Parse("#define TEST 2\n1+1=TEST\n"); | PreprocessorResult result = Parse("#define TEST 2\n1+1=TEST\n"); | ||||
TS_ASSERT_EQUALS(result.output, "\n1+1=2\n"); | TS_ASSERT_EQUALS(result.output, "\n1+1=2\n"); | ||||
} | } | ||||
void test_error() | void test_error() | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | void test_division_by_zero_error() | ||||
PreprocessorResult result = Parse(R"( | PreprocessorResult result = Parse(R"( | ||||
#if 2 / 0 | #if 2 / 0 | ||||
42 | 42 | ||||
#endif | #endif | ||||
)"); | )"); | ||||
TS_ASSERT_EQUALS(result.output.Trim(PS_TRIM_BOTH), ""); | TS_ASSERT_EQUALS(result.output.Trim(PS_TRIM_BOTH), ""); | ||||
TS_ASSERT_STR_CONTAINS(result.loggerOutput, "ERROR: Preprocessor error: line 2: Division by zero"); | TS_ASSERT_STR_CONTAINS(result.loggerOutput, "ERROR: Preprocessor error: line 2: Division by zero"); | ||||
} | } | ||||
void test_include() | |||||
{ | |||||
bool includeRetrieved = false; | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = [&includeRetrieved]( | |||||
const CStr& includePath, CStr& out) { | |||||
TS_ASSERT_EQUALS(includePath, "test.h"); | |||||
out = "42"; | |||||
includeRetrieved = true; | |||||
return true; | |||||
}; | |||||
PreprocessorResult result = ParseWithIncludes( | |||||
R"(#include "test.h" // Includes something.)", includeCallback); | |||||
TS_ASSERT(includeRetrieved); | |||||
TS_ASSERT_EQUALS(result.output.Trim(PS_TRIM_BOTH), "#line 1\n42\n#line 2"); | |||||
} | |||||
void test_include_double() | |||||
{ | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = []( | |||||
const CStr& includePath, CStr& out) { | |||||
TS_ASSERT_EQUALS(includePath, "test.h"); | |||||
out = "42"; | |||||
return true; | |||||
}; | |||||
PreprocessorResult result = ParseWithIncludes(R"( | |||||
#include "test.h" | |||||
#include "test.h" | |||||
#include "test.h" | |||||
)", includeCallback); | |||||
TS_ASSERT_EQUALS(result.output.Trim(PS_TRIM_BOTH), "#line 1\n42\n#line 3\n#line 1\n42\n#line 4\n#line 1\n42\n#line 5"); | |||||
} | |||||
void test_include_double_with_guards() | |||||
{ | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = []( | |||||
const CStr& includePath, CStr& out) { | |||||
TS_ASSERT_EQUALS(includePath, "test.h"); | |||||
out = R"(#ifndef INCLUDED_TEST | |||||
#define INCLUDED_TEST | |||||
42 | |||||
#endif)"; | |||||
return true; | |||||
}; | |||||
PreprocessorResult result = ParseWithIncludes(R"( | |||||
#include "test.h" | |||||
#include "test.h" | |||||
#include "test.h" | |||||
)", includeCallback); | |||||
TS_ASSERT_EQUALS(result.output.Trim(PS_TRIM_BOTH), "#line 1\n\n\t\t\t\t\n\t\t\t\t42\n\t\t\t\n#line 3\n#line 1\n\n\n\n\n#line 4\n#line 1\n\n\n\n\n#line 5"); | |||||
} | |||||
void test_include_invalid_argument() | |||||
{ | |||||
int includeRetrievedCounter = 0; | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = [&includeRetrievedCounter]( | |||||
const CStr& includePath, CStr& out) { | |||||
out = "42"; | |||||
++includeRetrievedCounter; | |||||
return true; | |||||
}; | |||||
PreprocessorResult result = ParseWithIncludes(R"( | |||||
#include <test.h> | |||||
#include test.h | |||||
)", includeCallback); | |||||
TS_ASSERT_EQUALS(includeRetrievedCounter, 0); | |||||
} | |||||
void test_include_invalid_file() | |||||
{ | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = []( | |||||
const CStr& includePath, CStr& out) { | |||||
return false; | |||||
}; | |||||
PreprocessorResult result = ParseWithIncludes(R"( | |||||
#include "missed_file.h" | |||||
)", includeCallback); | |||||
TS_ASSERT_STR_CONTAINS(result.loggerOutput, "ERROR: Preprocessor error: line 2: Can't load #include file: 'missed_file.h'"); | |||||
} | |||||
void test_include_with_defines() | |||||
{ | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = []( | |||||
const CStr& includePath, CStr& out) { | |||||
out = R"( | |||||
#if defined(A) | |||||
#define X 41 | |||||
#elif defined(B) | |||||
#define X 42 | |||||
#else | |||||
#define X 43 | |||||
#endif | |||||
#ifdef Y | |||||
#undef Y | |||||
#define Y 256 | |||||
#endif | |||||
vec3 color(); | |||||
)"; | |||||
return true; | |||||
}; | |||||
PreprocessorResult result = ParseWithIncludes(R"( | |||||
#define Y 128 | |||||
#define B 1 | |||||
#include "test.h" | |||||
X Y | |||||
)", includeCallback); | |||||
TS_ASSERT_EQUALS( | |||||
CompressWhiteSpaces(result.output.Trim(PS_TRIM_BOTH)), | |||||
"#line 1\n\n\t\n\n\n\t\n\t\n\n\n\t\n\t\n\t\n\t\n\tvec3 color();\n\t\n#line 5\n\t42 256"); | |||||
} | |||||
void test_performance_DISABLED() | |||||
{ | |||||
CPreprocessorWrapper::IncludeRetrieverCallback includeCallback = []( | |||||
const CStr& includePath, CStr& out) { | |||||
const size_t dotPosition = includePath.find('.'); | |||||
TS_ASSERT_DIFFERS(dotPosition, CStr::npos); | |||||
const int depth = CStr(includePath.substr(0, dotPosition)).ToInt(); | |||||
TS_ASSERT_LESS_THAN_EQUALS(0, depth); | |||||
if (depth < 4) | |||||
{ | |||||
std::stringstream nextIncludes; | |||||
for (int idx = 0; idx < 8; ++idx) | |||||
nextIncludes << "#include \"" << depth + 1 << ".h\"\n"; | |||||
out = nextIncludes.str(); | |||||
} | |||||
else | |||||
{ | |||||
out = R"( | |||||
42 | |||||
)"; | |||||
} | |||||
return true; | |||||
}; | |||||
const double start = timer_Time(); | |||||
PreprocessorResult result = ParseWithIncludes(R"( | |||||
#include "0.h" | |||||
)", includeCallback); | |||||
const double finish = timer_Time(); | |||||
printf("Total: %lfs\n", finish - start); | |||||
TS_ASSERT_EQUALS(result.output.Trim(PS_TRIM_BOTH).size(), 132824u); | |||||
} | |||||
}; | }; |
Wildfire Games · Phabricator