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