xref: /aosp_15_r20/external/skia/tests/SkSLErrorTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkNoDestructor.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOSFile.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramKind.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLUtil.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"  // IWYU pragma: keep
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkOSPath.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
22*c8dee2aaSAndroid Build Coastguard Worker 
23*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
24*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
25*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
26*c8dee2aaSAndroid Build Coastguard Worker #include <sstream>
27*c8dee2aaSAndroid Build Coastguard Worker #include <string>
28*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
29*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
30*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
33*c8dee2aaSAndroid Build Coastguard Worker 
get_expected_errors(const char * shaderString)34*c8dee2aaSAndroid Build Coastguard Worker static std::vector<std::string> get_expected_errors(const char* shaderString) {
35*c8dee2aaSAndroid Build Coastguard Worker     // Error expectations are embedded in the source with a special *%%* marker, like so:
36*c8dee2aaSAndroid Build Coastguard Worker     //
37*c8dee2aaSAndroid Build Coastguard Worker     //     /*%%*
38*c8dee2aaSAndroid Build Coastguard Worker     //     expected 'foo', but found 'bar'
39*c8dee2aaSAndroid Build Coastguard Worker     //     'baz' is not a valid identifier
40*c8dee2aaSAndroid Build Coastguard Worker     //     *%%*/
41*c8dee2aaSAndroid Build Coastguard Worker     //
42*c8dee2aaSAndroid Build Coastguard Worker     // Extract them from the shader text.
43*c8dee2aaSAndroid Build Coastguard Worker     std::vector<std::string> expectedErrors;
44*c8dee2aaSAndroid Build Coastguard Worker     constexpr char kExpectedErrorsStart[] = "/*%%*";
45*c8dee2aaSAndroid Build Coastguard Worker     constexpr char kExpectedErrorsEnd[] = "*%%*/";
46*c8dee2aaSAndroid Build Coastguard Worker     if (const char* startPtr = strstr(shaderString, kExpectedErrorsStart)) {
47*c8dee2aaSAndroid Build Coastguard Worker         startPtr += strlen(kExpectedErrorsStart);
48*c8dee2aaSAndroid Build Coastguard Worker         if (const char* endPtr = strstr(startPtr, kExpectedErrorsEnd)) {
49*c8dee2aaSAndroid Build Coastguard Worker             // Store the text between these delimiters in an array of expected errors.
50*c8dee2aaSAndroid Build Coastguard Worker             std::stringstream stream{std::string{startPtr, endPtr}};
51*c8dee2aaSAndroid Build Coastguard Worker             while (stream.good()) {
52*c8dee2aaSAndroid Build Coastguard Worker                 expectedErrors.push_back({});
53*c8dee2aaSAndroid Build Coastguard Worker                 std::getline(stream, expectedErrors.back(), '\n');
54*c8dee2aaSAndroid Build Coastguard Worker                 if (expectedErrors.back().empty()) {
55*c8dee2aaSAndroid Build Coastguard Worker                     expectedErrors.pop_back();
56*c8dee2aaSAndroid Build Coastguard Worker                 }
57*c8dee2aaSAndroid Build Coastguard Worker             }
58*c8dee2aaSAndroid Build Coastguard Worker         }
59*c8dee2aaSAndroid Build Coastguard Worker     }
60*c8dee2aaSAndroid Build Coastguard Worker 
61*c8dee2aaSAndroid Build Coastguard Worker     return expectedErrors;
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker 
check_expected_errors(skiatest::Reporter * r,const char * testFile,const std::vector<std::string> & expectedErrors,std::string reportedErrors)64*c8dee2aaSAndroid Build Coastguard Worker static void check_expected_errors(skiatest::Reporter* r,
65*c8dee2aaSAndroid Build Coastguard Worker                                   const char* testFile,
66*c8dee2aaSAndroid Build Coastguard Worker                                   const std::vector<std::string>& expectedErrors,
67*c8dee2aaSAndroid Build Coastguard Worker                                   std::string reportedErrors) {
68*c8dee2aaSAndroid Build Coastguard Worker     // Verify that the SkSL compiler actually emitted the expected error messages.
69*c8dee2aaSAndroid Build Coastguard Worker     // The list of expectations isn't necessarily exhaustive, though.
70*c8dee2aaSAndroid Build Coastguard Worker     std::string originalErrors = reportedErrors;
71*c8dee2aaSAndroid Build Coastguard Worker     bool reportOriginalErrors = false;
72*c8dee2aaSAndroid Build Coastguard Worker     for (const std::string& expectedError : expectedErrors) {
73*c8dee2aaSAndroid Build Coastguard Worker         // If this error wasn't reported, trigger an error.
74*c8dee2aaSAndroid Build Coastguard Worker         size_t pos = reportedErrors.find(expectedError.c_str());
75*c8dee2aaSAndroid Build Coastguard Worker         if (pos == std::string::npos) {
76*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "%s: Expected an error that wasn't reported:\n%s\n",
77*c8dee2aaSAndroid Build Coastguard Worker                    SkOSPath::Basename(testFile).c_str(), expectedError.c_str());
78*c8dee2aaSAndroid Build Coastguard Worker             reportOriginalErrors = true;
79*c8dee2aaSAndroid Build Coastguard Worker         } else {
80*c8dee2aaSAndroid Build Coastguard Worker             // We found the error that we expected to have. Remove that error from our report, and
81*c8dee2aaSAndroid Build Coastguard Worker             // everything preceding it as well. This ensures that we don't match the same error
82*c8dee2aaSAndroid Build Coastguard Worker             // twice, and that errors are reported in the order we expect.
83*c8dee2aaSAndroid Build Coastguard Worker             reportedErrors.erase(0, pos + expectedError.size());
84*c8dee2aaSAndroid Build Coastguard Worker         }
85*c8dee2aaSAndroid Build Coastguard Worker     }
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker     if (reportOriginalErrors) {
88*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "%s: The following errors were reported:\n%s\n",
89*c8dee2aaSAndroid Build Coastguard Worker                SkOSPath::Basename(testFile).c_str(), originalErrors.c_str());
90*c8dee2aaSAndroid Build Coastguard Worker     }
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker 
test_expect_fail(skiatest::Reporter * r,const char * testFile,SkSL::ProgramKind kind)93*c8dee2aaSAndroid Build Coastguard Worker static void test_expect_fail(skiatest::Reporter* r, const char* testFile, SkSL::ProgramKind kind) {
94*c8dee2aaSAndroid Build Coastguard Worker     // In a size-optimized build, there are a handful of errors which report differently, or not at
95*c8dee2aaSAndroid Build Coastguard Worker     // all. Skip over those tests.
96*c8dee2aaSAndroid Build Coastguard Worker     static const SkNoDestructor<THashSet<std::string_view>> kTestsToSkip{{
97*c8dee2aaSAndroid Build Coastguard Worker         // These are tests that have been deleted, but which may still show up (and fail) on tasks,
98*c8dee2aaSAndroid Build Coastguard Worker         // because the resources directory isn't properly cleaned up. (b/40044088)
99*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/InvalidBackendBindingFlagsGL.sksl",
100*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/InvalidThreadgroupRTS.rts",
101*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/LastFragColorWithoutCaps.sksl",
102*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/MeshFragmentWithShader.mfrag",
103*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/MeshFragmentWithBlender.mfrag",
104*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/MeshFragmentWithColorFilter.mfrag",
105*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/StaticIfTest.sksl",
106*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/StaticSwitchConditionalBreak.sksl",
107*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/StaticSwitchTest.sksl",
108*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/StaticSwitchWithConditionalBreak.sksl",
109*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/StaticSwitchWithConditionalContinue.sksl",
110*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/StaticSwitchWithConditionalReturn.sksl",
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/ComputeUniform.compute",
113*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/DuplicateBinding.compute",
114*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/InvalidThreadgroupCompute.compute",
115*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/UnspecifiedBinding.compute",
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ReservedNameISampler2D.rts",
118*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ProgramTooLarge_BlocklessLoops.rts",
119*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ProgramTooLarge_Extreme.rts",
120*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ProgramTooLarge_FlatLoop.rts",
121*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ProgramTooLarge_Functions.rts",
122*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ProgramTooLarge_NestedLoops.rts",
123*c8dee2aaSAndroid Build Coastguard Worker         "sksl/runtime_errors/ProgramTooLarge_SplitLoops.rts",
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_ENABLE_OPTIMIZE_SIZE
126*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/ArrayInlinedIndexOutOfRange.sksl",
127*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/MatrixInlinedIndexOutOfRange.sksl",
128*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/OverflowInlinedLiteral.sksl",
129*c8dee2aaSAndroid Build Coastguard Worker         "sksl/errors/VectorInlinedIndexOutOfRange.sksl",
130*c8dee2aaSAndroid Build Coastguard Worker #endif
131*c8dee2aaSAndroid Build Coastguard Worker     }};
132*c8dee2aaSAndroid Build Coastguard Worker     if (kTestsToSkip->contains(testFile)) {
133*c8dee2aaSAndroid Build Coastguard Worker         INFOF(r, "%s: skipped in SK_ENABLE_OPTIMIZE_SIZE mode", testFile);
134*c8dee2aaSAndroid Build Coastguard Worker         return;
135*c8dee2aaSAndroid Build Coastguard Worker     }
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> shaderData = GetResourceAsData(testFile);
138*c8dee2aaSAndroid Build Coastguard Worker     if (!shaderData) {
139*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "%s: Unable to load file", SkOSPath::Basename(testFile).c_str());
140*c8dee2aaSAndroid Build Coastguard Worker         return;
141*c8dee2aaSAndroid Build Coastguard Worker     }
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker     std::string shaderString{reinterpret_cast<const char*>(shaderData->bytes()),
144*c8dee2aaSAndroid Build Coastguard Worker                              shaderData->size()};
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker     std::vector<std::string> expectedErrors = get_expected_errors(shaderString.c_str());
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     // Compile the code.
149*c8dee2aaSAndroid Build Coastguard Worker     SkSL::Compiler compiler;
150*c8dee2aaSAndroid Build Coastguard Worker     SkSL::ProgramSettings settings;
151*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, std::move(shaderString),
152*c8dee2aaSAndroid Build Coastguard Worker                                                                      settings);
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker     // If the code actually generated a working program, we've already failed.
155*c8dee2aaSAndroid Build Coastguard Worker     if (program) {
156*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "%s: Expected failure, but compiled successfully",
157*c8dee2aaSAndroid Build Coastguard Worker                   SkOSPath::Basename(testFile).c_str());
158*c8dee2aaSAndroid Build Coastguard Worker         return;
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker     check_expected_errors(r, testFile, expectedErrors, compiler.errorText());
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker 
iterate_dir(const char * directory,const char * extension,const std::function<void (const char *)> & run)164*c8dee2aaSAndroid Build Coastguard Worker static void iterate_dir(const char* directory,
165*c8dee2aaSAndroid Build Coastguard Worker                         const char* extension,
166*c8dee2aaSAndroid Build Coastguard Worker                         const std::function<void(const char*)>& run) {
167*c8dee2aaSAndroid Build Coastguard Worker     SkString resourceDirectory = GetResourcePath(directory);
168*c8dee2aaSAndroid Build Coastguard Worker     SkOSFile::Iter iter(resourceDirectory.c_str(), extension);
169*c8dee2aaSAndroid Build Coastguard Worker     SkString name;
170*c8dee2aaSAndroid Build Coastguard Worker 
171*c8dee2aaSAndroid Build Coastguard Worker     while (iter.next(&name, /*getDir=*/false)) {
172*c8dee2aaSAndroid Build Coastguard Worker         SkString path(SkOSPath::Join(directory, name.c_str()));
173*c8dee2aaSAndroid Build Coastguard Worker         run(path.c_str());
174*c8dee2aaSAndroid Build Coastguard Worker     }
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLErrorTest,r)177*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLErrorTest, r) {
178*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/errors/", ".sksl", [&](const char* path) {
179*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kFragment);
180*c8dee2aaSAndroid Build Coastguard Worker     });
181*c8dee2aaSAndroid Build Coastguard Worker }
182*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLComputeErrorTest,r)183*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLComputeErrorTest, r) {
184*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/errors/", ".compute", [&](const char* path) {
185*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kCompute);
186*c8dee2aaSAndroid Build Coastguard Worker     });
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLMeshVertexErrorTest,r)189*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLMeshVertexErrorTest, r) {
190*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/errors/", ".mvert", [&](const char* path) {
191*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kMeshVertex);
192*c8dee2aaSAndroid Build Coastguard Worker     });
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLMeshFragmentErrorTest,r)195*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLMeshFragmentErrorTest, r) {
196*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/errors/", ".mfrag", [&](const char* path) {
197*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kMeshFragment);
198*c8dee2aaSAndroid Build Coastguard Worker     });
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLRuntimeShaderErrorTest,r)201*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLRuntimeShaderErrorTest, r) {
202*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/errors/", ".rts", [&](const char* path) {
203*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeShader);
204*c8dee2aaSAndroid Build Coastguard Worker     });
205*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/runtime_errors/", ".rts", [&](const char* path) {
206*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeShader);
207*c8dee2aaSAndroid Build Coastguard Worker     });
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLPrivateRuntimeShaderErrorTest,r)210*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLPrivateRuntimeShaderErrorTest, r) {
211*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/errors/", ".privrts", [&](const char* path) {
212*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kPrivateRuntimeShader);
213*c8dee2aaSAndroid Build Coastguard Worker     });
214*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/runtime_errors/", ".privrts", [&](const char* path) {
215*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kPrivateRuntimeShader);
216*c8dee2aaSAndroid Build Coastguard Worker     });
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLRuntimeColorFilterErrorTest,r)219*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLRuntimeColorFilterErrorTest, r) {
220*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/runtime_errors/", ".rtcf", [&](const char* path) {
221*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeColorFilter);
222*c8dee2aaSAndroid Build Coastguard Worker     });
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkSLRuntimeBlenderErrorTest,r)225*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkSLRuntimeBlenderErrorTest, r) {
226*c8dee2aaSAndroid Build Coastguard Worker     iterate_dir("sksl/runtime_errors/", ".rtb", [&](const char* path) {
227*c8dee2aaSAndroid Build Coastguard Worker         test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeBlender);
228*c8dee2aaSAndroid Build Coastguard Worker     });
229*c8dee2aaSAndroid Build Coastguard Worker }
230