blob: 98d8d32a999feb6a2cd6006c89126db30256e970 [file] [log] [blame]
// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Common validation fixtures for unit tests
#ifndef TEST_VAL_VAL_FIXTURES_H_
#define TEST_VAL_VAL_FIXTURES_H_
#include <memory>
#include <string>
#include "source/val/validation_state.h"
#include "spirv-tools/libspirv.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtest {
template <typename T>
class ValidateBase : public ::testing::Test,
public ::testing::WithParamInterface<T> {
public:
ValidateBase();
virtual void TearDown();
// Returns the a spv_const_binary struct
spv_const_binary get_const_binary();
// Assembles the given SPIR-V text, checks that it fails to assemble,
// and returns resulting diagnostic. No internal state is updated.
// Setting the desired_result to SPV_SUCCESS is used to allow all results
std::string CompileFailure(std::string code,
spv_target_env env = SPV_ENV_UNIVERSAL_1_0,
spv_result_t desired_result = SPV_SUCCESS);
// Checks that 'code' is valid SPIR-V text representation and stores the
// binary version for further method calls.
void CompileSuccessfully(std::string code,
spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
// Overwrites the word at index 'index' with the given word.
// For testing purposes, it is often useful to be able to manipulate the
// assembled binary before running the validator on it.
// This function overwrites the word at the given index with a new word.
void OverwriteAssembledBinary(uint32_t index, uint32_t word);
// Performs validation on the SPIR-V code.
spv_result_t ValidateInstructions(spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
// Performs validation. Returns the status and stores validation state into
// the vstate_ member.
spv_result_t ValidateAndRetrieveValidationState(
spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
// Destroys the stored binary.
void DestroyBinary() {
spvBinaryDestroy(binary_);
binary_ = nullptr;
}
// Destroys the stored diagnostic.
void DestroyDiagnostic() {
spvDiagnosticDestroy(diagnostic_);
diagnostic_ = nullptr;
}
std::string getDiagnosticString();
spv_position_t getErrorPosition();
spv_validator_options getValidatorOptions();
spv_binary binary_;
spv_diagnostic diagnostic_;
spv_validator_options options_;
std::unique_ptr<spvtools::val::ValidationState_t> vstate_;
};
template <typename T>
ValidateBase<T>::ValidateBase() : binary_(nullptr), diagnostic_(nullptr) {
// Initialize to default command line options. Different tests can then
// specialize specific options as necessary.
options_ = spvValidatorOptionsCreate();
}
template <typename T>
spv_const_binary ValidateBase<T>::get_const_binary() {
return spv_const_binary(binary_);
}
template <typename T>
void ValidateBase<T>::TearDown() {
if (diagnostic_) {
spvDiagnosticPrint(diagnostic_);
}
DestroyBinary();
DestroyDiagnostic();
spvValidatorOptionsDestroy(options_);
}
template <typename T>
std::string ValidateBase<T>::CompileFailure(std::string code,
spv_target_env env,
spv_result_t desired_result) {
spv_diagnostic diagnostic = nullptr;
spv_result_t actual_result =
spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(),
&binary_, &diagnostic);
EXPECT_NE(SPV_SUCCESS, actual_result);
// optional check for exact result
if (desired_result != SPV_SUCCESS) {
EXPECT_EQ(actual_result, desired_result);
}
std::string result(diagnostic->error);
spvDiagnosticDestroy(diagnostic);
return result;
}
template <typename T>
void ValidateBase<T>::CompileSuccessfully(std::string code,
spv_target_env env) {
DestroyBinary();
spv_diagnostic diagnostic = nullptr;
ScopedContext context(env);
auto status = spvTextToBinary(context.context, code.c_str(), code.size(),
&binary_, &diagnostic);
EXPECT_EQ(SPV_SUCCESS, status)
<< "ERROR: " << diagnostic->error
<< "\nSPIR-V could not be compiled into binary:\n"
<< code;
ASSERT_EQ(SPV_SUCCESS, status);
spvDiagnosticDestroy(diagnostic);
}
template <typename T>
void ValidateBase<T>::OverwriteAssembledBinary(uint32_t index, uint32_t word) {
ASSERT_TRUE(index < binary_->wordCount)
<< "OverwriteAssembledBinary: The given index is larger than the binary "
"word count.";
binary_->code[index] = word;
}
template <typename T>
spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
DestroyDiagnostic();
if (binary_ == nullptr) {
fprintf(stderr,
"ERROR: Attempting to validate a null binary, did you forget to "
"call CompileSuccessfully?");
fflush(stderr);
}
assert(binary_ != nullptr);
return spvValidateWithOptions(ScopedContext(env).context, options_,
get_const_binary(), &diagnostic_);
}
template <typename T>
spv_result_t ValidateBase<T>::ValidateAndRetrieveValidationState(
spv_target_env env) {
DestroyDiagnostic();
return spvtools::val::ValidateBinaryAndKeepValidationState(
ScopedContext(env).context, options_, get_const_binary()->code,
get_const_binary()->wordCount, &diagnostic_, &vstate_);
}
template <typename T>
std::string ValidateBase<T>::getDiagnosticString() {
return diagnostic_ == nullptr ? std::string()
: std::string(diagnostic_->error);
}
template <typename T>
spv_validator_options ValidateBase<T>::getValidatorOptions() {
return options_;
}
template <typename T>
spv_position_t ValidateBase<T>::getErrorPosition() {
return diagnostic_ == nullptr ? spv_position_t() : diagnostic_->position;
}
} // namespace spvtest
// For Vulkan testing.
// Allows test parameter test to list all possible VUIDs with a delimiter that
// is then split here to check if one VUID was in the error message
MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") {
// use space as delimiter because clang-format will properly line break VUID
// strings which is important the entire VUID is in a single line for script
// to scan
std::string delimiter = " ";
std::string token;
std::string vuids = std::string(vuid_set);
size_t position;
// Catch case were someone accidentally left spaces by trimming string
// clang-format off
vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) {
return (c != ' ');
}).base(), vuids.end());
vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) {
return (c != ' ');
}));
// clang-format on
do {
position = vuids.find(delimiter);
if (position != std::string::npos) {
token = vuids.substr(0, position);
vuids.erase(0, position + delimiter.length());
} else {
token = vuids.substr(0); // last item
}
// arg contains diagnostic message
if (arg.find(token) != std::string::npos) {
return true;
}
} while (position != std::string::npos);
return false;
}
#endif // TEST_VAL_VAL_FIXTURES_H_