Add tests for ShaderManager

pull/578/head
elsid 5 years ago
parent ca649003ed
commit 1f3dfaedcc
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -27,6 +27,10 @@ if (GTEST_FOUND AND GMOCK_FOUND)
detournavigator/tilecachedrecastmeshmanager.cpp
settings/parser.cpp
shader/parsedefines.cpp
shader/parsefors.cpp
shader/shadermanager.cpp
)
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})

@ -0,0 +1,191 @@
#include <components/shader/shadermanager.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
namespace
{
using namespace testing;
using namespace Shader;
using DefineMap = ShaderManager::DefineMap;
struct ShaderParseDefinesTest : Test
{
std::string mSource;
const std::string mName = "shader";
DefineMap mDefines;
DefineMap mGlobalDefines;
};
TEST_F(ShaderParseDefinesTest, empty_should_succeed)
{
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "");
}
TEST_F(ShaderParseDefinesTest, should_fail_for_absent_define)
{
mSource = "@foo\n";
ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "@foo\n");
}
TEST_F(ShaderParseDefinesTest, should_replace_by_existing_define)
{
mDefines["foo"] = "42";
mSource = "@foo\n";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "42\n");
}
TEST_F(ShaderParseDefinesTest, should_replace_by_existing_global_define)
{
mGlobalDefines["foo"] = "42";
mSource = "@foo\n";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "42\n");
}
TEST_F(ShaderParseDefinesTest, should_prefer_define_over_global_define)
{
mDefines["foo"] = "13";
mGlobalDefines["foo"] = "42";
mSource = "@foo\n";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "13\n");
}
namespace SupportedTerminals
{
struct ShaderParseDefinesTest : ::ShaderParseDefinesTest, WithParamInterface<char> {};
TEST_P(ShaderParseDefinesTest, support_defines_terminated_by)
{
mDefines["foo"] = "13";
mSource = "@foo" + std::string(1, GetParam());
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "13" + std::string(1, GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
SupportedTerminals,
ShaderParseDefinesTest,
Values(' ', '\n', '\r', '(', ')', '[', ']', '.', ';', ',')
);
}
TEST_F(ShaderParseDefinesTest, should_not_support_define_ending_with_source)
{
mDefines["foo"] = "42";
mSource = "@foo";
ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "@foo");
}
TEST_F(ShaderParseDefinesTest, should_replace_all_matched_values)
{
mDefines["foo"] = "42";
mSource = "@foo @foo ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "42 42 ");
}
TEST_F(ShaderParseDefinesTest, should_support_define_with_empty_name)
{
mDefines[""] = "42";
mSource = "@ ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "42 ");
}
TEST_F(ShaderParseDefinesTest, should_replace_all_found_defines)
{
mDefines["foo"] = "42";
mDefines["bar"] = "13";
mDefines["baz"] = "55";
mSource = "@foo @bar ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "42 13 ");
}
TEST_F(ShaderParseDefinesTest, should_fail_on_foreach_without_endforeach)
{
mSource = "@foreach ";
ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach ");
}
TEST_F(ShaderParseDefinesTest, should_fail_on_endforeach_without_foreach)
{
mSource = "@endforeach ";
ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_replace_at_sign_by_dollar_for_foreach_endforeach)
{
mSource = "@foreach @endforeach ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_succeed_on_unmatched_nested_foreach)
{
mSource = "@foreach @foreach @endforeach ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach $foreach $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_fail_on_unmatched_nested_endforeach)
{
mSource = "@foreach @endforeach @endforeach ";
ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach $endforeach $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_support_nested_foreach)
{
mSource = "@foreach @foreach @endforeach @endforeach ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach $foreach $endforeach $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_support_foreach_variable)
{
mSource = "@foreach foo @foo @endforeach ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach foo $foo $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_not_replace_foreach_variable_by_define)
{
mDefines["foo"] = "42";
mSource = "@foreach foo @foo @endforeach ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach foo $foo $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_support_nested_foreach_with_variable)
{
mSource = "@foreach foo @foo @foreach bar @bar @endforeach @endforeach ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "$foreach foo $foo $foreach bar $bar $endforeach $endforeach ");
}
TEST_F(ShaderParseDefinesTest, should_not_support_single_line_comments_for_defines)
{
mDefines["foo"] = "42";
mSource = "@foo // @foo\n";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "42 // 42\n");
}
TEST_F(ShaderParseDefinesTest, should_not_support_multiline_comments_for_defines)
{
mDefines["foo"] = "42";
mSource = "/* @foo */ @foo ";
ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));
EXPECT_EQ(mSource, "/* 42 */ 42 ");
}
}

@ -0,0 +1,94 @@
#include <components/shader/shadermanager.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
namespace
{
using namespace testing;
using namespace Shader;
using DefineMap = ShaderManager::DefineMap;
struct ShaderParseForsTest : Test
{
std::string mSource;
const std::string mName = "shader";
};
TEST_F(ShaderParseForsTest, empty_should_succeed)
{
ASSERT_TRUE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "");
}
TEST_F(ShaderParseForsTest, should_fail_for_single_escape_symbol)
{
mSource = "$";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "$");
}
TEST_F(ShaderParseForsTest, should_fail_on_first_found_escaped_not_foreach)
{
mSource = "$foo ";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "$foo ");
}
TEST_F(ShaderParseForsTest, should_fail_on_absent_foreach_variable)
{
mSource = "$foreach ";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "$foreach ");
}
TEST_F(ShaderParseForsTest, should_fail_on_unmatched_after_variable)
{
mSource = "$foreach foo ";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "$foreach foo ");
}
TEST_F(ShaderParseForsTest, should_fail_on_absent_newline_after_foreach_list)
{
mSource = "$foreach foo 1,2,3 ";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "$foreach foo 1,2,3 ");
}
TEST_F(ShaderParseForsTest, should_fail_on_absent_endforeach_after_newline)
{
mSource = "$foreach foo 1,2,3\n";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "$foreach foo 1,2,3\n");
}
TEST_F(ShaderParseForsTest, should_replace_complete_foreach_by_line_number)
{
mSource = "$foreach foo 1,2,3\n$endforeach";
ASSERT_TRUE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "\n#line 3");
}
TEST_F(ShaderParseForsTest, should_replace_loop_variable)
{
mSource = "$foreach foo 1,2,3\n$foo\n$endforeach";
ASSERT_TRUE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "1\n2\n3\n\n#line 4");
}
TEST_F(ShaderParseForsTest, should_count_line_number_from_existing)
{
mSource = "$foreach foo 1,2,3\n#line 10\n$foo\n$endforeach";
ASSERT_TRUE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "#line 10\n1\n#line 10\n2\n#line 10\n3\n\n#line 12");
}
TEST_F(ShaderParseForsTest, should_not_support_nested_loops)
{
mSource = "$foreach foo 1,2\n$foo\n$foreach bar 1,2\n$bar\n$endforeach\n$endforeach";
ASSERT_FALSE(parseFors(mSource, mName));
EXPECT_EQ(mSource, "1\n1\n2\n$foreach bar 1,2\n1\n\n#line 6\n2\n2\n$foreach bar 1,2\n2\n\n#line 6\n\n#line 7");
}
}

@ -0,0 +1,240 @@
#include <components/shader/shadermanager.hpp>
#include <boost/filesystem/fstream.hpp>
#include <gtest/gtest.h>
namespace
{
using namespace testing;
using namespace Shader;
struct ShaderManagerTest : Test
{
ShaderManager mManager;
ShaderManager::DefineMap mDefines;
ShaderManagerTest()
{
mManager.setShaderPath(".");
}
template <class F>
void withShaderFile(const std::string& content, F&& f)
{
withShaderFile("", content, std::forward<F>(f));
}
template <class F>
void withShaderFile(const std::string& suffix, const std::string& content, F&& f)
{
const auto path = UnitTest::GetInstance()->current_test_info()->name() + suffix + ".glsl";
{
boost::filesystem::ofstream stream;
stream.open(path);
stream << content;
stream.close();
}
f(path);
}
};
TEST_F(ShaderManagerTest, get_shader_with_empty_content_should_succeed)
{
const std::string content;
withShaderFile(content, [this] (const std::string& templateName) {
EXPECT_TRUE(mManager.getShader(templateName, {}, osg::Shader::VERTEX));
});
}
TEST_F(ShaderManagerTest, get_shader_should_not_change_source_without_template_parameters)
{
const std::string content =
"#version 120\n"
"void main() {}\n";
withShaderFile(content, [&] (const std::string& templateName) {
const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);
ASSERT_TRUE(shader);
EXPECT_EQ(shader->getShaderSource(), content);
});
}
TEST_F(ShaderManagerTest, get_shader_should_replace_includes_with_content)
{
const std::string content0 =
"void foo() {}\n";
withShaderFile("_0", content0, [&] (const std::string& templateName0) {
const std::string content1 =
"#include \"" + templateName0 + "\"\n"
"void bar() { foo() }\n";
withShaderFile("_1", content1, [&] (const std::string& templateName1) {
const std::string content2 =
"#version 120\n"
"#include \"" + templateName1 + "\"\n"
"void main() { bar() }\n";
withShaderFile(content2, [&] (const std::string& templateName2) {
const auto shader = mManager.getShader(templateName2, mDefines, osg::Shader::VERTEX);
ASSERT_TRUE(shader);
const std::string expected =
"#version 120\n"
"#line 0 1\n"
"#line 0 2\n"
"void foo() {}\n"
"\n"
"#line 0 0\n"
"\n"
"void bar() { foo() }\n"
"\n"
"#line 2 0\n"
"\n"
"void main() { bar() }\n";
EXPECT_EQ(shader->getShaderSource(), expected);
});
});
});
}
TEST_F(ShaderManagerTest, get_shader_should_replace_defines)
{
const std::string content =
"#version 120\n"
"#define FLAG @flag\n"
"void main() {}\n"
;
withShaderFile(content, [&] (const std::string& templateName) {
mDefines["flag"] = "1";
const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);
ASSERT_TRUE(shader);
const std::string expected =
"#version 120\n"
"#define FLAG 1\n"
"void main() {}\n";
EXPECT_EQ(shader->getShaderSource(), expected);
});
}
TEST_F(ShaderManagerTest, get_shader_should_expand_loop)
{
const std::string content =
"#version 120\n"
"@foreach index @list\n"
" varying vec4 foo@index;\n"
"@endforeach\n"
"void main() {}\n"
;
withShaderFile(content, [&] (const std::string& templateName) {
mDefines["list"] = "1,2,3";
const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);
ASSERT_TRUE(shader);
const std::string expected =
"#version 120\n"
" varying vec4 foo1;\n"
" varying vec4 foo2;\n"
" varying vec4 foo3;\n"
"\n"
"#line 5\n"
"void main() {}\n";
EXPECT_EQ(shader->getShaderSource(), expected);
});
}
TEST_F(ShaderManagerTest, get_shader_should_replace_loops_with_conditions)
{
const std::string content =
"#version 120\n"
"@foreach index @list\n"
" varying vec4 foo@index;\n"
"@endforeach\n"
"void main()\n"
"{\n"
"#ifdef BAR\n"
"@foreach index @list\n"
" foo@index = vec4(1.0);\n"
"@endforeach\n"
"#elif BAZ\n"
"@foreach index @list\n"
" foo@index = vec4(2.0);\n"
"@endforeach\n"
"#else\n"
"@foreach index @list\n"
" foo@index = vec4(3.0);\n"
"@endforeach\n"
"#endif\n"
"}\n"
;
withShaderFile(content, [&] (const std::string& templateName) {
mDefines["list"] = "1,2,3";
const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);
ASSERT_TRUE(shader);
const std::string expected =
"#version 120\n"
" varying vec4 foo1;\n"
" varying vec4 foo2;\n"
" varying vec4 foo3;\n"
"\n"
"#line 5\n"
"void main()\n"
"{\n"
"#ifdef BAR\n"
" foo1 = vec4(1.0);\n"
" foo2 = vec4(1.0);\n"
" foo3 = vec4(1.0);\n"
"\n"
"#line 11\n"
"#elif BAZ\n"
"#line 12\n"
" foo1 = vec4(2.0);\n"
" foo2 = vec4(2.0);\n"
" foo3 = vec4(2.0);\n"
"\n"
"#line 15\n"
"#else\n"
"#line 16\n"
" foo1 = vec4(3.0);\n"
" foo2 = vec4(3.0);\n"
" foo3 = vec4(3.0);\n"
"\n"
"#line 19\n"
"#endif\n"
"#line 20\n"
"}\n";
EXPECT_EQ(shader->getShaderSource(), expected);
});
}
TEST_F(ShaderManagerTest, get_shader_should_fail_on_absent_template_parameters_in_single_line_comments)
{
const std::string content =
"#version 120\n"
"// #define FLAG @flag\n"
"void main() {}\n"
;
withShaderFile(content, [&] (const std::string& templateName) {
EXPECT_FALSE(mManager.getShader(templateName, mDefines, osg::Shader::VERTEX));
});
}
TEST_F(ShaderManagerTest, get_shader_should_fail_on_absent_template_parameter_in_multi_line_comments)
{
const std::string content =
"#version 120\n"
"/* #define FLAG @flag */\n"
"void main() {}\n"
;
withShaderFile(content, [&] (const std::string& templateName) {
EXPECT_FALSE(mManager.getShader(templateName, mDefines, osg::Shader::VERTEX));
});
}
}

@ -63,6 +63,10 @@ namespace Shader
OpenThreads::Mutex mMutex;
};
bool parseFors(std::string& source, const std::string& templateName);
bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines,
const ShaderManager::DefineMap& globalDefines, const std::string& templateName);
}
#endif

Loading…
Cancel
Save