namespace {
using spvtest::ScopedContext;
+using std::get;
using std::make_pair;
+using std::ostringstream;
using std::pair;
using std::string;
-using std::stringstream;
using std::tuple;
using std::vector;
using testing::Combine;
using testing::Values;
using testing::ValuesIn;
-using ValidateCapability =
- spvtest::ValidateBase<tuple<string, pair<string, vector<string>>>>;
+// Parameter for validation test fixtures. The first string is a capability
+// name that will begin the assembly under test, the second the remainder
+// assembly, and the vector at the end determines whether the test expects
+// success or failure. See below for details and convenience methods to access
+// each one.
+//
+// The assembly to test is composed from a variable top line and a fixed
+// remainder. The top line will be an OpCapability instruction, while the
+// remainder will be some assembly text that succeeds or fails to assemble
+// depending on which capability was chosen. For instance, the following will
+// succeed:
+//
+// OpCapability Pipes ; implies Kernel
+// OpLifetimeStop %1 0 ; requires Kernel
+//
+// and the following will fail:
+//
+// OpCapability Kernel
+// %1 = OpTypeNamedBarrier ; requires NamedBarrier
+//
+// So how does the test parameter capture which capabilities should cause
+// success and which shouldn't? The answer is in the last element: it's a
+// vector of capabilities that make the remainder assembly succeed. So if the
+// first-line capability exists in that vector, success is expected; otherwise,
+// failure is expected in the tests.
+//
+// We will use testing::Combine() to vary the first line: when we combine
+// AllCapabilities() with a single remainder assembly, we generate enough test
+// cases to try the assembly with every possible capability that could be
+// declared. However, Combine() only produces tuples -- it cannot produce, say,
+// a struct. Therefore, this type must be a tuple.
+using CapTestParameter = tuple<string, pair<string, vector<string>>>;
+
+const string& Capability(const CapTestParameter& p) { return get<0>(p); }
+const string& Remainder(const CapTestParameter& p) { return get<1>(p).first; }
+const vector<string>& MustSucceed(const CapTestParameter& p) {
+ return get<1>(p).second;
+}
+
+// Creates assembly to test from p.
+string MakeAssembly(const CapTestParameter& p) {
+ ostringstream ss;
+ const string& capability = Capability(p);
+ if (!capability.empty()) {
+ ss << "OpCapability " << capability << "\n";
+ }
+ ss << Remainder(p);
+ return ss.str();
+}
+
+// Expected validation result for p.
+spv_result_t ExpectedResult(const CapTestParameter& p) {
+ const auto& caps = MustSucceed(p);
+ auto found = find(begin(caps), end(caps), Capability(p));
+ return (found == end(caps)) ? SPV_ERROR_INVALID_CAPABILITY : SPV_SUCCESS;
+}
+
+// Assembles using v1.0, unless the parameter's capability requires v1.1.
+using ValidateCapability = spvtest::ValidateBase<CapTestParameter>;
+
+// Always assembles using v1.1.
+using ValidateCapabilityV11 = spvtest::ValidateBase<CapTestParameter>;
TEST_F(ValidateCapability, Default) {
const char str[] = R"(
"StorageImageReadWithoutFormat",
"StorageImageWriteWithoutFormat",
"MultiViewport",
- "SubgroupDispatch"};
+ "SubgroupDispatch",
+ "NamedBarrier",
+ "PipeStorage"};
return *r;
}
"DeviceEnqueue",
"LiteralSampler",
"Int8",
- "SubgroupDispatch"};
+ "SubgroupDispatch",
+ "NamedBarrier",
+ "PipeStorage"};
return *r;
}
make_pair(string(kGLSL450MemoryModel) +
"OpEntryPoint Kernel %func \"shader\" "
"OpExecutionMode %func ContractionOff" +
- string(kVoidFVoid), KernelDependencies())
-)),);
+ string(kVoidFVoid), KernelDependencies()))),);
+
+// clang-format on
+
+INSTANTIATE_TEST_CASE_P(
+ ExecutionModeV11, ValidateCapabilityV11,
+ Combine(ValuesIn(AllCapabilities()),
+ Values(make_pair(string(kOpenCLMemoryModel) +
+ "OpEntryPoint Kernel %func \"shader\" "
+ "OpExecutionMode %func SubgroupSize 1" +
+ string(kVoidFVoid),
+ vector<string>{"SubgroupDispatch"}),
+ make_pair(
+ string(kOpenCLMemoryModel) +
+ "OpEntryPoint Kernel %func \"shader\" "
+ "OpExecutionMode %func SubgroupsPerWorkgroup 65535" +
+ string(kVoidFVoid),
+ vector<string>{"SubgroupDispatch"}))), );
+// clang-format off
INSTANTIATE_TEST_CASE_P(StorageClass, ValidateCapability,
Combine(
// that can be used for operands where IDs are required. The assembly is valid,
// apart from not declaring any capabilities required by the operands.
string ImageOperandsTemplate(const string& operands) {
- stringstream ss;
+ ostringstream ss;
// clang-format off
ss << R"(
OpCapability Kernel
}
TEST_P(ValidateCapability, Capability) {
- string capability;
- pair<string, vector<string>> operation;
- std::tie(capability, operation) = GetParam();
- stringstream ss;
- if (!capability.empty()) {
- ss << "OpCapability " + capability + " ";
- }
-
- ss << operation.first;
-
- spv_result_t res = SPV_ERROR_INTERNAL;
- auto& valid_capabilities = operation.second;
-
- auto it =
- find(begin(valid_capabilities), end(valid_capabilities), capability);
- if (it != end(valid_capabilities)) {
- res = SPV_SUCCESS;
- } else {
- res = SPV_ERROR_INVALID_CAPABILITY;
- }
-
+ const string capability = Capability(GetParam());
spv_target_env env =
(capability.empty() || Exists(capability, SPV_ENV_UNIVERSAL_1_0))
? SPV_ENV_UNIVERSAL_1_0
: SPV_ENV_UNIVERSAL_1_1;
- CompileSuccessfully(ss.str(), env);
- ASSERT_EQ(res, ValidateInstructions(env)) << ss.str();
+ const string test_code = MakeAssembly(GetParam());
+ CompileSuccessfully(test_code, env);
+ ASSERT_EQ(ExpectedResult(GetParam()), ValidateInstructions(env)) << test_code;
+}
+
+TEST_P(ValidateCapabilityV11, Capability) {
+ const string test_code = MakeAssembly(GetParam());
+ CompileSuccessfully(test_code, SPV_ENV_UNIVERSAL_1_1);
+ ASSERT_EQ(ExpectedResult(GetParam()),
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_1))
+ << test_code;
}
} // namespace anonymous