]> git.tdb.fi Git - libs/gl.git/blob - tests/glsl/glslcompiler.cpp
Add a unit test framework and some tests for the GLSL compiler
[libs/gl.git] / tests / glsl / glslcompiler.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/fs/dir.h>
3 #include <msp/fs/utils.h>
4 #include <msp/gl/glsl/compiler.h>
5 #include <msp/gl/glsl/glsl_error.h>
6 #include <msp/gl/glsl/tokenizer.h>
7 #include <msp/strings/utils.h>
8 #include <msp/test/test.h>
9
10 #include <msp/io/print.h>
11
12 class GlslCompilerTest: public Msp::Test::RegisteredTest<GlslCompilerTest>
13 {
14 private:
15         struct TestCase
16         {
17                 std::string name;
18                 std::string source;
19                 std::map<Msp::GL::SL::Stage::Type, std::string> expected_output;
20         };
21
22         std::list<TestCase> test_cases;
23
24 public:
25         GlslCompilerTest();
26
27         static const char *get_name() { return "GLSL compiler"; }
28
29 private:
30         const TestCase &load_test_case(const std::string &);
31
32         void run_test_case(const TestCase *);
33 };
34
35 using namespace std;
36 using namespace Msp;
37
38 GlslCompilerTest::GlslCompilerTest()
39 {
40         FS::Path tests_dir = "glsl";
41         list<string> test_files = FS::list_filtered(tests_dir, "\\.glsl$");
42         test_files.sort();
43         for(const auto &fn: test_files)
44                 load_test_case((tests_dir/fn).str());
45
46         for(const auto &tc: test_cases)
47                 add(&GlslCompilerTest::run_test_case, &tc, tc.name);
48 }
49
50 const GlslCompilerTest::TestCase &GlslCompilerTest::load_test_case(const string &fn)
51 {
52         IO::BufferedFile file(fn);
53         TestCase test_case;
54         test_case.name = FS::basename(fn);
55         string *target = &test_case.source;
56         while(!file.eof())
57         {
58                 string line;
59                 if(!file.getline(line))
60                         break;
61
62                 if(line=="*/")
63                         continue;
64
65                 string::size_type expected = line.find("Expected output:");
66                 if(expected!=string::npos)
67                 {
68                         string stage = strip(line.substr(expected+16));
69                         if(stage=="vertex")
70                                 target = &test_case.expected_output[GL::SL::Stage::VERTEX];
71                         else if(stage=="geometry")
72                                 target = &test_case.expected_output[GL::SL::Stage::GEOMETRY];
73                         else if(stage=="fragment")
74                                 target = &test_case.expected_output[GL::SL::Stage::FRAGMENT];
75                         else
76                                 throw runtime_error("Unknown stage "+stage);
77                         continue;
78                 }
79
80                 *target += line;
81                 *target += '\n';
82         }
83         test_cases.push_back(test_case);
84
85         return test_cases.back();
86 }
87
88 void GlslCompilerTest::run_test_case(const TestCase *test_case)
89 {
90         GL::SL::Compiler compiler(GL::SL::Features::all());
91         compiler.set_source(test_case->source, "<test>");
92         compiler.compile(GL::SL::Compiler::PROGRAM);
93
94         auto stages = compiler.get_stages();
95         for(auto s: stages)
96         {
97                 auto i = test_case->expected_output.find(s);
98                 if(i==test_case->expected_output.end())
99                         fail(format("Compiler produced extra stage %s", GL::SL::Stage::get_stage_name(s)));
100
101                 string output = compiler.get_stage_glsl(s);
102                 debug(format("Output for stage %s:", GL::SL::Stage::get_stage_name(s)));
103                 auto lines = split_fields(output, '\n');
104                 for(unsigned j=0; j<lines.size(); ++j)
105                         debug(format("%3d: %s", j+1, lines[j]));
106
107                 GL::SL::Tokenizer tokenizer;
108                 tokenizer.begin("<output>", output);
109
110                 GL::SL::Tokenizer expected_tkn;
111                 expected_tkn.begin("<expected>", i->second);
112
113                 while(1)
114                 {
115                         string token = expected_tkn.parse_token();
116
117                         try
118                         {
119                                 tokenizer.expect(token);
120                         }
121                         catch(const GL::SL::invalid_shader_source &exc)
122                         {
123                                 fail(exc.what());
124                         }
125
126                         if(token.empty())
127                                 break;
128                 }
129         }
130
131         for(const auto &s: test_case->expected_output)
132                 if(find(stages, s.first)==stages.end())
133                         fail(format("Compiler didn't produce stage %s", GL::SL::Stage::get_stage_name(s.first)));
134 }