]> git.tdb.fi Git - libs/gl.git/blobdiff - tests/glsl/glslcompiler.cpp
Add a unit test framework and some tests for the GLSL compiler
[libs/gl.git] / tests / glsl / glslcompiler.cpp
diff --git a/tests/glsl/glslcompiler.cpp b/tests/glsl/glslcompiler.cpp
new file mode 100644 (file)
index 0000000..57911cc
--- /dev/null
@@ -0,0 +1,134 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/utils.h>
+#include <msp/gl/glsl/compiler.h>
+#include <msp/gl/glsl/glsl_error.h>
+#include <msp/gl/glsl/tokenizer.h>
+#include <msp/strings/utils.h>
+#include <msp/test/test.h>
+
+#include <msp/io/print.h>
+
+class GlslCompilerTest: public Msp::Test::RegisteredTest<GlslCompilerTest>
+{
+private:
+       struct TestCase
+       {
+               std::string name;
+               std::string source;
+               std::map<Msp::GL::SL::Stage::Type, std::string> expected_output;
+       };
+
+       std::list<TestCase> test_cases;
+
+public:
+       GlslCompilerTest();
+
+       static const char *get_name() { return "GLSL compiler"; }
+
+private:
+       const TestCase &load_test_case(const std::string &);
+
+       void run_test_case(const TestCase *);
+};
+
+using namespace std;
+using namespace Msp;
+
+GlslCompilerTest::GlslCompilerTest()
+{
+       FS::Path tests_dir = "glsl";
+       list<string> test_files = FS::list_filtered(tests_dir, "\\.glsl$");
+       test_files.sort();
+       for(const auto &fn: test_files)
+               load_test_case((tests_dir/fn).str());
+
+       for(const auto &tc: test_cases)
+               add(&GlslCompilerTest::run_test_case, &tc, tc.name);
+}
+
+const GlslCompilerTest::TestCase &GlslCompilerTest::load_test_case(const string &fn)
+{
+       IO::BufferedFile file(fn);
+       TestCase test_case;
+       test_case.name = FS::basename(fn);
+       string *target = &test_case.source;
+       while(!file.eof())
+       {
+               string line;
+               if(!file.getline(line))
+                       break;
+
+               if(line=="*/")
+                       continue;
+
+               string::size_type expected = line.find("Expected output:");
+               if(expected!=string::npos)
+               {
+                       string stage = strip(line.substr(expected+16));
+                       if(stage=="vertex")
+                               target = &test_case.expected_output[GL::SL::Stage::VERTEX];
+                       else if(stage=="geometry")
+                               target = &test_case.expected_output[GL::SL::Stage::GEOMETRY];
+                       else if(stage=="fragment")
+                               target = &test_case.expected_output[GL::SL::Stage::FRAGMENT];
+                       else
+                               throw runtime_error("Unknown stage "+stage);
+                       continue;
+               }
+
+               *target += line;
+               *target += '\n';
+       }
+       test_cases.push_back(test_case);
+
+       return test_cases.back();
+}
+
+void GlslCompilerTest::run_test_case(const TestCase *test_case)
+{
+       GL::SL::Compiler compiler(GL::SL::Features::all());
+       compiler.set_source(test_case->source, "<test>");
+       compiler.compile(GL::SL::Compiler::PROGRAM);
+
+       auto stages = compiler.get_stages();
+       for(auto s: stages)
+       {
+               auto i = test_case->expected_output.find(s);
+               if(i==test_case->expected_output.end())
+                       fail(format("Compiler produced extra stage %s", GL::SL::Stage::get_stage_name(s)));
+
+               string output = compiler.get_stage_glsl(s);
+               debug(format("Output for stage %s:", GL::SL::Stage::get_stage_name(s)));
+               auto lines = split_fields(output, '\n');
+               for(unsigned j=0; j<lines.size(); ++j)
+                       debug(format("%3d: %s", j+1, lines[j]));
+
+               GL::SL::Tokenizer tokenizer;
+               tokenizer.begin("<output>", output);
+
+               GL::SL::Tokenizer expected_tkn;
+               expected_tkn.begin("<expected>", i->second);
+
+               while(1)
+               {
+                       string token = expected_tkn.parse_token();
+
+                       try
+                       {
+                               tokenizer.expect(token);
+                       }
+                       catch(const GL::SL::invalid_shader_source &exc)
+                       {
+                               fail(exc.what());
+                       }
+
+                       if(token.empty())
+                               break;
+               }
+       }
+
+       for(const auto &s: test_case->expected_output)
+               if(find(stages, s.first)==stages.end())
+                       fail(format("Compiler didn't produce stage %s", GL::SL::Stage::get_stage_name(s.first)));
+}