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:
John Stiles 2022-02-07 23:45:07 -05:00 committed by SkCQ
parent f1bb464ee4
commit 8d646c127a
15 changed files with 205 additions and 11 deletions

View File

@ -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",

View File

@ -5,3 +5,7 @@ float foo(float x) {
void main() {
float x = foo(1, 2);
}
/*%%*
call to 'foo' expected 1 argument, but found 2
*%%*/

View File

@ -5,3 +5,7 @@ float foo(float x) {
void main() {
float x = foo(true);
}
/*%%*
expected 'float', but found 'bool'
*%%*/

View File

@ -1,2 +1,6 @@
void test(int x);
void test(out int x) {}
/*%%*
modifiers on parameter 1 differ between declaration and definition
*%%*/

View File

@ -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)
*%%*/

View File

@ -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 '[]'
*%%*/

View File

@ -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]'
*%%*/

View File

@ -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]'
*%%*/

View File

@ -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
*%%*/

View File

@ -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
*%%*/

View File

@ -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]'
*%%*/

View File

@ -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
View 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);
});
}

View File

@ -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

View File

@ -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