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