Verify that tests in errors/ actually generate the expected errors.
Error expectations are embedded in the source with a special *%%* marker, like this: /*%%* expected 'foo', but found 'bar' 'baz' is not a valid identifier *%%*/ This unit test compiles every effect in errors/ and verifies that it makes an error. It also verifies that the errors returned include the expectations from the *%%* marker section, in the listed order, if any expectations have been listed. (Error expectations are not meant to be exhaustive; additional errors are allowed.) In this CL, I've manually attached error expectations to the first few error tests. A followup CL will (mechanically) add expectations to every error test, based on their current error reports. Change-Id: I4add30fef6419c4d3f8d2a221c5aeb53eee35ae7 Bug: skia:12665 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/505399 Auto-Submit: John Stiles <johnstiles@google.com> Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
f1bb464ee4
commit
8d646c127a
@ -247,6 +247,7 @@ tests_sources = [
|
||||
"$_tests/SkSLDSLOnlyTest.cpp",
|
||||
"$_tests/SkSLDSLTest.cpp",
|
||||
"$_tests/SkSLES2ConformanceTest.cpp",
|
||||
"$_tests/SkSLErrorTest.cpp",
|
||||
"$_tests/SkSLGLSLTestbed.cpp",
|
||||
"$_tests/SkSLInterpreterTest.cpp",
|
||||
"$_tests/SkSLMemoryLayoutTest.cpp",
|
||||
|
@ -5,3 +5,7 @@ float foo(float x) {
|
||||
void main() {
|
||||
float x = foo(1, 2);
|
||||
}
|
||||
|
||||
/*%%*
|
||||
call to 'foo' expected 1 argument, but found 2
|
||||
*%%*/
|
||||
|
@ -5,3 +5,7 @@ float foo(float x) {
|
||||
void main() {
|
||||
float x = foo(true);
|
||||
}
|
||||
|
||||
/*%%*
|
||||
expected 'float', but found 'bool'
|
||||
*%%*/
|
||||
|
@ -1,2 +1,6 @@
|
||||
void test(int x);
|
||||
void test(out int x) {}
|
||||
|
||||
/*%%*
|
||||
modifiers on parameter 1 differ between declaration and definition
|
||||
*%%*/
|
||||
|
@ -1,6 +1,10 @@
|
||||
// Expect 3 errors
|
||||
|
||||
float noElements[2] = float[2]();
|
||||
float notEnoughElements[2] = float[2](1);
|
||||
float rightNumberOfElements[2] = float[2](1, 2);
|
||||
float tooManyElements[2] = float[2](1, 2, 3);
|
||||
|
||||
/*%%*
|
||||
invalid arguments to 'float[2]' constructor (expected 2 elements, but found 0)
|
||||
invalid arguments to 'float[2]' constructor (expected 2 elements, but found 1)
|
||||
invalid arguments to 'float[2]' constructor (expected 2 elements, but found 3)
|
||||
*%%*/
|
||||
|
@ -5,3 +5,11 @@ void array_123 () { half4x4 a[123]; half4x4 v = a[123]; }
|
||||
void array_huge () { int4 a[123]; int4 v = a[1000000000]; }
|
||||
void array_overflow () { half3 a[123]; half3 v = a[3000000000]; }
|
||||
void array_no_index () { int a[123]; int v = a[]; }
|
||||
|
||||
/*%%*
|
||||
index -1 out of range for 'int[123]'
|
||||
index 123 out of range for 'half4x4[123]'
|
||||
index 1000000000 out of range for 'int4[123]'
|
||||
index 3000000000 out of range for 'half3[123]'
|
||||
missing index in '[]'
|
||||
*%%*/
|
||||
|
@ -11,3 +11,8 @@ half4 main(float2 coords) {
|
||||
int undefined = indexArray(-1) + indexArray(3);
|
||||
return colorGreen;
|
||||
}
|
||||
|
||||
/*%%*
|
||||
index -1 out of range for 'int[3]'
|
||||
index 3 out of range for 'int[3]'
|
||||
*%%*/
|
||||
|
@ -4,3 +4,12 @@ void array_negate_float () { float a[123]; -a; }
|
||||
void array_negate_half3 () { half3 a[123]; -a; }
|
||||
void array_negate_bool2 () { bool2 a[123]; -a; }
|
||||
void array_negate_half4x4() { half4x4 a[123]; -a; }
|
||||
|
||||
/*%%*
|
||||
'-' cannot operate on 'int[123]'
|
||||
'-' cannot operate on 'int4[123]'
|
||||
'-' cannot operate on 'float[123]'
|
||||
'-' cannot operate on 'half3[123]'
|
||||
'-' cannot operate on 'bool2[123]'
|
||||
'-' cannot operate on 'half4x4[123]'
|
||||
*%%*/
|
||||
|
@ -21,3 +21,29 @@ void h2() { float x[int2(2, 2)]; }
|
||||
void i2() { float x[]; }
|
||||
void j2() { float x[int3(4000000000)]; }
|
||||
void k2() { float x[int(1e20)]; }
|
||||
|
||||
/*%%*
|
||||
array size must be positive
|
||||
array size must be positive
|
||||
array size must be positive
|
||||
expected 'int', but found 'float'
|
||||
integer is out of range for type 'int': 4000000000
|
||||
array size must be positive
|
||||
expected 'int', but found 'bool'
|
||||
expected 'int', but found 'bool'
|
||||
expected 'int', but found 'int2'
|
||||
missing index in '[]'
|
||||
integer is out of range for type 'int': 4000000000
|
||||
integer is out of range for type 'int': -9223372036854775808
|
||||
array size must be positive
|
||||
array size must be positive
|
||||
array size must be positive
|
||||
array size must be an integer
|
||||
array size out of bounds
|
||||
array size must be an integer
|
||||
array size must be an integer
|
||||
array size must be an integer
|
||||
expected array dimension
|
||||
integer is out of range for type 'int': 4000000000
|
||||
integer is out of range for type 'int': -9223372036854775808
|
||||
*%%*/
|
||||
|
@ -1,5 +1,3 @@
|
||||
// Expect 6 errors
|
||||
|
||||
void a[2];
|
||||
void[2] b;
|
||||
|
||||
@ -7,3 +5,13 @@ void[2] funcF() {}
|
||||
void funcG() { void g[2]; }
|
||||
void funcH() { void[2] h; }
|
||||
void funcI() { void[2]; }
|
||||
|
||||
/*%%*
|
||||
type 'void' may not be used in an array
|
||||
type 'void' may not be used in an array
|
||||
type 'void' may not be used in an array
|
||||
function 'funcF' can exit without returning a value
|
||||
type 'void' may not be used in an array
|
||||
type 'void' may not be used in an array
|
||||
type 'void' may not be used in an array
|
||||
*%%*/
|
||||
|
@ -4,3 +4,12 @@ void array_plus_float () { float a[123]; +a; }
|
||||
void array_plus_half3 () { half3 a[123]; +a; }
|
||||
void array_plus_bool2 () { bool2 a[123]; +a; }
|
||||
void array_plus_half4x4() { half4x4 a[123]; +a; }
|
||||
|
||||
/*%%*
|
||||
'+' cannot operate on 'int[123]'
|
||||
'+' cannot operate on 'int4[123]'
|
||||
'+' cannot operate on 'float[123]'
|
||||
'+' cannot operate on 'half3[123]'
|
||||
'+' cannot operate on 'bool2[123]'
|
||||
'+' cannot operate on 'half4x4[123]'
|
||||
*%%*/
|
||||
|
@ -1,2 +1,7 @@
|
||||
float4x4[2] return_float4x4_2() {}
|
||||
int[1] return_int_1() {}
|
||||
|
||||
/*%%*
|
||||
functions may not return type 'float4x4[2]'
|
||||
functions may not return type 'int[1]'
|
||||
*%%*/
|
||||
|
107
tests/SkSLErrorTest.cpp
Normal file
107
tests/SkSLErrorTest.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 "gm/gm.h"
|
||||
#include "include/effects/SkRuntimeEffect.h"
|
||||
#include "src/core/SkOSFile.h"
|
||||
#include "src/core/SkRuntimeEffectPriv.h"
|
||||
#include "src/gpu/GrCaps.h"
|
||||
#include "src/gpu/GrDirectContextPriv.h"
|
||||
#include "src/sksl/SkSLCompiler.h"
|
||||
#include "src/utils/SkOSPath.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/Resources.h"
|
||||
#include "tools/ToolUtils.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
|
||||
static void test_expect_fail(skiatest::Reporter* r, const char* testFile) {
|
||||
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()};
|
||||
|
||||
// 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.c_str(), 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(SkSL::ProgramKind::kFragment,
|
||||
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;
|
||||
}
|
||||
|
||||
// Verify that the SkSL compiler actually emitted the expected error messages.
|
||||
// The list of expectations isn't necessarily exhaustive, though.
|
||||
std::string reportedErrors = compiler.errorText();
|
||||
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\"",
|
||||
SkOSPath::Basename(testFile).c_str(), expectedError.c_str());
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void iterate_dir(const char* directory, const std::function<void(const char*)>& run) {
|
||||
SkString resourceDirectory = GetResourcePath(directory);
|
||||
SkOSFile::Iter iter(resourceDirectory.c_str(), ".sksl");
|
||||
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/", [&](const char* path) {
|
||||
test_expect_fail(r, path);
|
||||
});
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
### Compilation failed:
|
||||
|
||||
error: 3: invalid arguments to 'float[2]' constructor (expected 2 elements, but found 0)
|
||||
error: 4: invalid arguments to 'float[2]' constructor (expected 2 elements, but found 1)
|
||||
error: 6: invalid arguments to 'float[2]' constructor (expected 2 elements, but found 3)
|
||||
error: 1: invalid arguments to 'float[2]' constructor (expected 2 elements, but found 0)
|
||||
error: 2: invalid arguments to 'float[2]' constructor (expected 2 elements, but found 1)
|
||||
error: 4: invalid arguments to 'float[2]' constructor (expected 2 elements, but found 3)
|
||||
3 errors
|
||||
|
@ -1,10 +1,10 @@
|
||||
### Compilation failed:
|
||||
|
||||
error: 3: type 'void' may not be used in an array
|
||||
error: 1: type 'void' may not be used in an array
|
||||
error: 2: type 'void' may not be used in an array
|
||||
error: 4: type 'void' may not be used in an array
|
||||
error: 4: function 'funcF' can exit without returning a value
|
||||
error: 5: type 'void' may not be used in an array
|
||||
error: 6: type 'void' may not be used in an array
|
||||
error: 6: function 'funcF' can exit without returning a value
|
||||
error: 7: type 'void' may not be used in an array
|
||||
error: 8: type 'void' may not be used in an array
|
||||
error: 9: type 'void' may not be used in an array
|
||||
7 errors
|
||||
|
Loading…
Reference in New Issue
Block a user