Add v1.1 capabilities validation.
authorDejan Mircevski <deki@google.com>
Fri, 29 Apr 2016 17:40:01 +0000 (13:40 -0400)
committerDejan Mircevski <deki@google.com>
Fri, 29 Apr 2016 17:40:01 +0000 (13:40 -0400)
Refactor the ValidateCapability test fixture.

Explain the meaning of test parameters.  Factor out methods for
convenience and readability.  DRY v1.0 and v1.1 tests.

test/Validate.Capability.cpp

index d9dc3d8..5d4e96b 100644 (file)
 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"(
@@ -127,7 +188,9 @@ const vector<string>& AllCapabilities() {
     "StorageImageReadWithoutFormat",
     "StorageImageWriteWithoutFormat",
     "MultiViewport",
-    "SubgroupDispatch"};
+    "SubgroupDispatch",
+    "NamedBarrier",
+    "PipeStorage"};
   return *r;
 }
 
@@ -255,7 +318,9 @@ const vector<string>& KernelDependencies() {
   "DeviceEnqueue",
   "LiteralSampler",
   "Int8",
-  "SubgroupDispatch"};
+  "SubgroupDispatch",
+  "NamedBarrier",
+  "PipeStorage"};
   return *r;
 }
 
@@ -489,8 +554,25 @@ make_pair(string(kGLSL450MemoryModel) +
 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(
@@ -912,7 +994,7 @@ make_pair(string(kOpenCLMemoryModel) +
 // 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
@@ -965,33 +1047,22 @@ bool Exists(const std::string& capability, spv_target_env env) {
 }
 
 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