1beeab415c
This is useful because it will allow these tests to be supported in Android CTS, where only Runtime Effects are API-accessible. This CL updates the C++ test harness so that .rts files in the error/ directory are found, and tweaks error tests as necessary to make them Runtime Effect-compatible. For instance, Runtime Effects enforce the parameters on main(), which adds extra errors that we don't want. And some error tests require ES3 (e.g. array constructors) and so those tests remain as .sksl files. In this CL, only tests beginning with A are updated. The remaining tests will be updated in followup CLs. Change-Id: I70b064df4f0b3ed02d6bc8cc9add7ee844a78691 Bug: skia:13042 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/522424 Auto-Submit: John Stiles <johnstiles@google.com> Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
154 lines
5.7 KiB
C++
154 lines
5.7 KiB
C++
/*
|
|
* Copyright 2022 Google LLC
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "include/core/SkData.h"
|
|
#include "include/core/SkRefCnt.h"
|
|
#include "include/core/SkString.h"
|
|
#include "include/private/SkSLProgramKind.h"
|
|
#include "src/core/SkOSFile.h"
|
|
#include "src/sksl/SkSLCompiler.h"
|
|
#include "src/sksl/SkSLUtil.h"
|
|
#include "src/sksl/ir/SkSLProgram.h"
|
|
#include "src/utils/SkOSPath.h"
|
|
#include "tests/Test.h"
|
|
#include "tools/Resources.h"
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
static std::vector<std::string> get_expected_errors(const char* shaderString) {
|
|
// Error expectations are embedded in the source with a special *%%* marker, like so:
|
|
//
|
|
// /*%%*
|
|
// expected 'foo', but found 'bar'
|
|
// 'baz' is not a valid identifier
|
|
// *%%*/
|
|
//
|
|
// Extract them from the shader text.
|
|
std::vector<std::string> expectedErrors;
|
|
constexpr char kExpectedErrorsStart[] = "/*%%*";
|
|
constexpr char kExpectedErrorsEnd[] = "*%%*/";
|
|
if (const char* startPtr = strstr(shaderString, kExpectedErrorsStart)) {
|
|
startPtr += strlen(kExpectedErrorsStart);
|
|
if (const char* endPtr = strstr(startPtr, kExpectedErrorsEnd)) {
|
|
// Store the text between these delimiters in an array of expected errors.
|
|
std::stringstream stream{std::string{startPtr, endPtr}};
|
|
while (stream.good()) {
|
|
expectedErrors.push_back({});
|
|
std::getline(stream, expectedErrors.back(), '\n');
|
|
if (expectedErrors.back().empty()) {
|
|
expectedErrors.pop_back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return expectedErrors;
|
|
}
|
|
|
|
static void check_expected_errors(skiatest::Reporter* r,
|
|
const char* testFile,
|
|
const std::vector<std::string>& expectedErrors,
|
|
std::string reportedErrors) {
|
|
// Verify that the SkSL compiler actually emitted the expected error messages.
|
|
// The list of expectations isn't necessarily exhaustive, though.
|
|
std::string originalErrors = reportedErrors;
|
|
bool reportOriginalErrors = false;
|
|
for (const std::string& expectedError : expectedErrors) {
|
|
// If this error wasn't reported, trigger an error.
|
|
size_t pos = reportedErrors.find(expectedError.c_str());
|
|
if (pos == std::string::npos) {
|
|
ERRORF(r, "%s: Expected an error that wasn't reported:\n%s\n",
|
|
SkOSPath::Basename(testFile).c_str(), expectedError.c_str());
|
|
reportOriginalErrors = true;
|
|
} else {
|
|
// We found the error that we expected to have. Remove that error from our report, and
|
|
// everything preceding it as well. This ensures that we don't match the same error
|
|
// twice, and that errors are reported in the order we expect.
|
|
reportedErrors.erase(0, pos + expectedError.size());
|
|
}
|
|
}
|
|
|
|
if (reportOriginalErrors) {
|
|
ERRORF(r, "%s: The following errors were reported:\n%s\n",
|
|
SkOSPath::Basename(testFile).c_str(), originalErrors.c_str());
|
|
}
|
|
}
|
|
|
|
static void test_expect_fail(skiatest::Reporter* r, const char* testFile, SkSL::ProgramKind kind) {
|
|
sk_sp<SkData> shaderData = GetResourceAsData(testFile);
|
|
if (!shaderData) {
|
|
ERRORF(r, "%s: Unable to load file", SkOSPath::Basename(testFile).c_str());
|
|
return;
|
|
}
|
|
|
|
std::string shaderString{reinterpret_cast<const char*>(shaderData->bytes()),
|
|
shaderData->size()};
|
|
|
|
std::vector<std::string> expectedErrors = get_expected_errors(shaderString.c_str());
|
|
|
|
// Compile the code.
|
|
std::unique_ptr<SkSL::ShaderCaps> caps = SkSL::ShaderCapsFactory::Standalone();
|
|
SkSL::Compiler compiler(caps.get());
|
|
SkSL::Program::Settings settings;
|
|
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, std::move(shaderString),
|
|
settings);
|
|
|
|
// If the code actually generated a working program, we've already failed.
|
|
if (program) {
|
|
ERRORF(r, "%s: Expected failure, but compiled successfully",
|
|
SkOSPath::Basename(testFile).c_str());
|
|
return;
|
|
}
|
|
|
|
check_expected_errors(r, testFile, expectedErrors, compiler.errorText());
|
|
}
|
|
|
|
static void iterate_dir(const char* directory,
|
|
const char* extension,
|
|
const std::function<void(const char*)>& run) {
|
|
SkString resourceDirectory = GetResourcePath(directory);
|
|
SkOSFile::Iter iter(resourceDirectory.c_str(), extension);
|
|
SkString name;
|
|
|
|
while (iter.next(&name, /*getDir=*/false)) {
|
|
SkString path(SkOSPath::Join(directory, name.c_str()));
|
|
run(path.c_str());
|
|
}
|
|
}
|
|
|
|
DEF_TEST(SkSLErrorTest, r) {
|
|
iterate_dir("sksl/errors/", ".sksl", [&](const char* path) {
|
|
test_expect_fail(r, path, SkSL::ProgramKind::kFragment);
|
|
});
|
|
iterate_dir("sksl/errors/", ".rts", [&](const char* path) {
|
|
test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeShader);
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLRuntimeShaderErrorTest, r) {
|
|
iterate_dir("sksl/runtime_errors/", ".rts", [&](const char* path) {
|
|
test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeShader);
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLRuntimeColorFilterErrorTest, r) {
|
|
iterate_dir("sksl/runtime_errors/", ".rtcf", [&](const char* path) {
|
|
test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeColorFilter);
|
|
});
|
|
}
|
|
|
|
DEF_TEST(SkSLRuntimeBlenderErrorTest, r) {
|
|
iterate_dir("sksl/runtime_errors/", ".rtb", [&](const char* path) {
|
|
test_expect_fail(r, path, SkSL::ProgramKind::kRuntimeBlender);
|
|
});
|
|
}
|