// Copyright (c) 2016 Google 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.
#include "gtest/gtest.h"
#include "source/table.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace {
// TODO(antiagainst): Use public C API for setting the consumer once exists.
#ifndef SPIRV_TOOLS_SHAREDLIB
void SetContextMessageConsumer(spv_context context, MessageConsumer consumer) {
spvtools::SetContextMessageConsumer(context, consumer);
}
#else
void SetContextMessageConsumer(spv_context, MessageConsumer) {}
#endif
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForValidInput) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] =
"OpCapability Shader\n"
"OpCapability Linkage\n"
"OpMemoryModel Logical GLSL450";
spv_binary binary = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
{
// Sadly the compiler don't allow me to feed binary directly to
// spvValidate().
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_SUCCESS, spvValidate(context, &b, nullptr));
}
spv_text text = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context, binary->code,
binary->wordCount, 0, &text, nullptr));
spvTextDestroy(text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidAssembling) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] = "%1 = OpName";
spv_binary binary = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
nullptr));
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidDiassembling) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] = "OpNop";
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
// Change OpNop to an invalid (wordcount|opcode) word.
binary->code[binary->wordCount - 1] = 0xffffffff;
spv_text text = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
nullptr));
spvTextDestroy(text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidValidating) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] = "OpNop";
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr));
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) {
const char input_text[] = "%1 = OpName\n";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
// The error happens at scanning the begining of second line.
EXPECT_STREQ("input", source);
EXPECT_EQ(1u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(12u, position.index);
EXPECT_STREQ("Expected operand, found end of stream.", message);
});
spv_binary binary = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
nullptr));
#ifndef SPIRV_TOOLS_SHAREDLIB
EXPECT_EQ(1, invocation);
#endif
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerNullDiagnosticForDisassembling) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(1u, position.index);
EXPECT_STREQ("Invalid opcode: 65535", message);
});
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
// Change OpNop to an invalid (wordcount|opcode) word.
binary->code[binary->wordCount - 1] = 0xffffffff;
spv_text text = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
nullptr));
#ifndef SPIRV_TOOLS_SHAREDLIB
EXPECT_EQ(1, invocation);
#endif
spvTextDestroy(text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerNullDiagnosticForValidating) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
// TODO(antiagainst): what validation reports is not a word offset here.
// It is inconsistent with diassembler. Should be fixed.
EXPECT_EQ(1u, position.index);
EXPECT_STREQ(
"Nop cannot appear before the memory model instruction\n"
" OpNop\n",
message);
});
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr));
#ifndef SPIRV_TOOLS_SHAREDLIB
EXPECT_EQ(1, invocation);
#endif
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// When having both a consumer and an diagnostic object, the diagnostic object
// should take priority.
TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) {
const char input_text[] = "%1 = OpName";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t, const char*, const spv_position_t&,
const char*) { ++invocation; });
spv_binary binary = nullptr;
spv_diagnostic diagnostic = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
&diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForDisassembling) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t, const char*, const spv_position_t&,
const char*) { ++invocation; });
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
// Change OpNop to an invalid (wordcount|opcode) word.
binary->code[binary->wordCount - 1] = 0xffffffff;
spv_diagnostic diagnostic = nullptr;
spv_text text = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
&diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
EXPECT_STREQ("Invalid opcode: 65535", diagnostic->error);
spvTextDestroy(text);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForValidating) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t, const char*, const spv_position_t&,
const char*) { ++invocation; });
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
spv_diagnostic diagnostic = nullptr;
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
EXPECT_STREQ(
"Nop cannot appear before the memory model instruction\n"
" OpNop\n",
diagnostic->error);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
} // namespace
} // namespace spvtools