• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "gm/gm.h"
9 #include "include/effects/SkRuntimeEffect.h"
10 #include "src/core/SkOSFile.h"
11 #include "src/core/SkRuntimeEffectPriv.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrDirectContextPriv.h"
14 #include "src/sksl/SkSLCompiler.h"
15 #include "src/utils/SkOSPath.h"
16 #include "tests/Test.h"
17 #include "tools/Resources.h"
18 #include "tools/ToolUtils.h"
19 
20 #include <sstream>
21 #include <string_view>
22 
test_expect_fail(skiatest::Reporter * r,const char * testFile,SkSL::ProgramKind kind)23 static void test_expect_fail(skiatest::Reporter* r, const char* testFile, SkSL::ProgramKind kind) {
24     sk_sp<SkData> shaderData = GetResourceAsData(testFile);
25     if (!shaderData) {
26         ERRORF(r, "%s: Unable to load file", SkOSPath::Basename(testFile).c_str());
27         return;
28     }
29 
30     std::string shaderString{reinterpret_cast<const char*>(shaderData->bytes()),
31                              shaderData->size()};
32 
33     // Error expectations are embedded in the source with a special *%%* marker, like so:
34     //
35     //     /*%%*
36     //     expected 'foo', but found 'bar'
37     //     'baz' is not a valid identifier
38     //     *%%*/
39     //
40     // Extract them from the shader text.
41     std::vector<std::string> expectedErrors;
42     constexpr char kExpectedErrorsStart[] = "/*%%*";
43     constexpr char kExpectedErrorsEnd[] = "*%%*/";
44     if (const char* startPtr = strstr(shaderString.c_str(), kExpectedErrorsStart)) {
45         startPtr += strlen(kExpectedErrorsStart);
46         if (const char* endPtr = strstr(startPtr, kExpectedErrorsEnd)) {
47             // Store the text between these delimiters in an array of expected errors.
48             std::stringstream stream{std::string{startPtr, endPtr}};
49             while (stream.good()) {
50                 expectedErrors.push_back({});
51                 std::getline(stream, expectedErrors.back(), '\n');
52                 if (expectedErrors.back().empty()) {
53                     expectedErrors.pop_back();
54                 }
55             }
56         }
57     }
58 
59     // Compile the code.
60     std::unique_ptr<SkSL::ShaderCaps> caps = SkSL::ShaderCapsFactory::Standalone();
61     SkSL::Compiler compiler(caps.get());
62     SkSL::Program::Settings settings;
63     std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, std::move(shaderString),
64                                                                      settings);
65 
66     // If the code actually generated a working program, we've already failed.
67     if (program) {
68         ERRORF(r, "%s: Expected failure, but compiled successfully",
69                   SkOSPath::Basename(testFile).c_str());
70         return;
71     }
72 
73     // Verify that the SkSL compiler actually emitted the expected error messages.
74     // The list of expectations isn't necessarily exhaustive, though.
75     std::string reportedErrors = compiler.errorText();
76     std::string originalErrors = reportedErrors;
77     bool reportOriginalErrors = false;
78     for (const std::string& expectedError : expectedErrors) {
79         // If this error wasn't reported, trigger an error.
80         size_t pos = reportedErrors.find(expectedError.c_str());
81         if (pos == std::string::npos) {
82             ERRORF(r, "%s: Expected an error that wasn't reported:\n%s\n",
83                    SkOSPath::Basename(testFile).c_str(), expectedError.c_str());
84             reportOriginalErrors = true;
85         } else {
86             // We found the error that we expected to have. Remove that error from our report, and
87             // everything preceding it as well. This ensures that we don't match the same error
88             // twice, and that errors are reported in the order we expect.
89             reportedErrors.erase(0, pos + expectedError.size());
90         }
91     }
92 
93     if (reportOriginalErrors) {
94         ERRORF(r, "%s: The following errors were reported:\n%s\n",
95                SkOSPath::Basename(testFile).c_str(), originalErrors.c_str());
96     }
97 }
98 
iterate_dir(const char * directory,const char * extension,const std::function<void (const char *)> & run)99 static void iterate_dir(const char* directory,
100                         const char* extension,
101                         const std::function<void(const char*)>& run) {
102     SkString resourceDirectory = GetResourcePath(directory);
103     SkOSFile::Iter iter(resourceDirectory.c_str(), extension);
104     SkString name;
105 
106     while (iter.next(&name, /*getDir=*/false)) {
107         SkString path(SkOSPath::Join(directory, name.c_str()));
108         run(path.c_str());
109     }
110 }
111 
DEF_TEST(SkSLErrorTest,r)112 DEF_TEST(SkSLErrorTest, r) {
113     iterate_dir("sksl/errors/", ".sksl", [&](const char* path) {
114         test_expect_fail(r, path, SkSL::ProgramKind::kFragment);
115     });
116     iterate_dir("sksl/errors/", ".rts", [&](const char* path) {
117         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeShader);
118     });
119 }
120 
DEF_TEST(SkSLRuntimeShaderErrorTest,r)121 DEF_TEST(SkSLRuntimeShaderErrorTest, r) {
122     iterate_dir("sksl/runtime_errors/", ".rts", [&](const char* path) {
123         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeShader);
124     });
125 }
126 
DEF_TEST(SkSLRuntimeColorFilterErrorTest,r)127 DEF_TEST(SkSLRuntimeColorFilterErrorTest, r) {
128     iterate_dir("sksl/runtime_errors/", ".rtcf", [&](const char* path) {
129         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeColorFilter);
130     });
131 }
132 
DEF_TEST(SkSLRuntimeBlenderErrorTest,r)133 DEF_TEST(SkSLRuntimeBlenderErrorTest, r) {
134     iterate_dir("sksl/runtime_errors/", ".rtb", [&](const char* path) {
135         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeBlender);
136     });
137 }
138