blob: b1da3ed6f04867338c80110e147686755aa9bd29 [file]
// Copyright 2023 Google LLC
//
// 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
//
// https://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 "mpact/sim/decoder/bin_format_visitor.h"
#include <cstdlib>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
#include "absl/log/check.h"
#include "absl/log/log_sink_registry.h"
#include "absl/strings/str_cat.h"
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/decoder/test/log_sink.h"
#include "re2/re2.h"
namespace {
using mpact::sim::test::LogSink;
constexpr char kTestUndeclaredOutputsDir[] = "TEST_UNDECLARED_OUTPUTS_DIR";
constexpr char kBinFmtSyntaxErrorBaseName[] = "syntax_error";
constexpr char kBinFmtFormatErrorBaseName[] = "format_error";
constexpr char kBinFmtFormatErrorUndefBaseName[] = "format_error_undef";
constexpr char kConstraintsBaseName[] = "constraints";
constexpr char kConstraintsDecoderName[] = "Constraints";
constexpr char kDecoderName[] = "ErrorTest";
constexpr char kBaseName[] = "error_test";
constexpr char kEmptyDecoderName[] = "Empty";
constexpr char kEmptyBaseName[] = "empty_file";
constexpr char kRiscVDecoderName[] = "RiscV32G";
constexpr char kRiscVBaseName[] = "riscv32";
constexpr char kRiscVTopName[] = "riscv32_top.bin_fmt";
constexpr char kRiscV32GName[] = "riscv32g.bin_fmt";
constexpr char kRiscV32CName[] = "riscv32c.bin_fmt";
constexpr char kGeneratorBaseName[] = "generator";
constexpr char kGeneratorDecoderName[] = "Generator";
constexpr char kVliwBaseName[] = "vliw";
constexpr char kVliwDecoderName[] = "Vliw24";
constexpr char kInstructionGroupBaseName[] = "instruction_group";
constexpr char kInstructionGroupDecoderName[] = "InstructionGroup";
constexpr char kRecursiveExampleBaseName[] = "example_with_recursive_include";
constexpr char kInstructionGroupErrorsBaseName[] = "instruction_group_errors";
constexpr char kInstructionGroupErrorsDecoderName[] = "InstructionGroupErrors";
using ::mpact::sim::decoder::bin_format::BinFormatVisitor;
constexpr char kBinFmtPrefix[] = R"pre(
decoder Generator {
namespace sim::generator::encoding;
opcode_enum = "isa::OpcodeEnum";
RiscVGInst32;
};
format Inst32Format[32] {
fields:
unsigned bits[25];
unsigned opcode[7];
};
format BType[32] : Inst32Format {
fields:
unsigned imm7[7];
unsigned rs2[5];
unsigned rs1[5];
unsigned func3[3];
unsigned imm5[5];
unsigned opcode[7];
overlays:
signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;
};
instruction group RiscVGInst32[32] : Inst32Format {
)pre";
constexpr char kBinFmtSuffix[] = R"post(
};
)post";
// The depot path to the test directory.
constexpr char kDepotPath[] = "mpact/sim/decoder/test/";
std::string OutputDir() { return "./"; }
static bool FileExists(const std::string& name) {
std::ifstream file(name);
return file.good();
}
class BinFormatVisitorTest : public testing::Test {
protected:
BinFormatVisitorTest() {}
std::vector<std::string> paths_;
};
TEST_F(BinFormatVisitorTest, NullFileParsing) {
// Set up input and output file paths.
std::vector<std::string> input_files = {
absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = OutputDir();
BinFormatVisitor visitor;
// Parse and process the input file.
EXPECT_FALSE(visitor
.Process(input_files, kEmptyBaseName, kEmptyDecoderName,
paths_, output_dir)
.ok());
}
// Make sure recursive includes cause a failure.
TEST_F(BinFormatVisitorTest, RecursiveInclude) {
// Set up input and output file paths.
std::vector<std::string> input_files = {absl::StrCat(
kDepotPath, "testfiles/", kRecursiveExampleBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = OutputDir();
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
BinFormatVisitor visitor;
auto success = visitor
.Process(input_files, kRiscVDecoderName,
kRecursiveExampleBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_FALSE(success);
// Make sure the requisite error messageßs are present.
auto ptr = log_sink.error_log().find("Error: Recursive include of '");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, BasicParsing) {
// Make sure the visitor can read and parse the input file.
// Set up input and output file paths.
std::vector<std::string> input_files = {
absl::StrCat(kDepotPath, "testfiles/", kRiscVTopName),
absl::StrCat(kDepotPath, "testfiles/", kRiscV32GName),
absl::StrCat(kDepotPath, "testfiles/", kRiscV32CName),
};
ASSERT_TRUE(FileExists(input_files[0]));
ASSERT_TRUE(FileExists(input_files[1]));
ASSERT_TRUE(FileExists(input_files[2]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file.
EXPECT_TRUE(visitor
.Process(input_files, kRiscVDecoderName, kRiscVBaseName,
paths_, output_dir)
.ok());
// Verify that the decoder files _decoder.{.h,.cc} files were generated.
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kRiscVBaseName, "_bin_decoder.h")));
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kRiscVBaseName, "_bin_decoder.cc")));
}
// This tests the generator using non-tuple values.
TEST_F(BinFormatVisitorTest, SimpleGenerator) {
// Create the file.
std::string file_name =
testing::TempDir() + "/" + "duplicate_variable.bin_fmt";
std::ofstream output_file(file_name, std::ofstream::out);
CHECK(output_file.good());
output_file << kBinFmtPrefix;
output_file << R"mid(
GENERATE( btype = [0, 1, 2, 3, 4, "5", "6"]) {
b$(btype) : BType : func3 == $(btype), opcode == 0b110'0011;
};
)mid";
output_file << kBinFmtSuffix;
output_file.close();
std::vector<std::string> input_files = {file_name};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process(input_files, kGeneratorDecoderName,
kGeneratorBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_TRUE(success);
// Verify that the decoder files _bin_decoder.{.h,.cc} files were generated.
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kGeneratorBaseName, "_bin_decoder.h")));
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kGeneratorBaseName, "_bin_decoder.cc")));
std::ifstream decoder_file(
absl::StrCat(output_dir, "/", kGeneratorBaseName, "_bin_decoder.cc"));
CHECK(decoder_file.good());
// Verify that decoder entries include the instructions inside the GENERATE()
// construct.
std::string decoder_str((std::istreambuf_iterator<char>(decoder_file)),
(std::istreambuf_iterator<char>()));
for (auto name : {"kB0", "kB1", "kB2", "kB3", "kB4", "kB5", "kB6"}) {
RE2 re(name);
EXPECT_TRUE(RE2::PartialMatch(decoder_str, re)) << name;
}
}
TEST_F(BinFormatVisitorTest, Generator) {
std::string input_file =
absl::StrCat(kDepotPath, "testfiles/", kGeneratorBaseName, ".bin_fmt");
ASSERT_TRUE(FileExists(input_file));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process({input_file}, kGeneratorDecoderName,
kGeneratorBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_TRUE(success);
// Make sure the warning about unreferenced binding variable is found.
auto ptr =
log_sink.warning_log().find("Unreferenced binding variable 'unused'");
EXPECT_TRUE(ptr != std::string::npos);
// Verify that the decoder files _bin_decoder.{.h,.cc} files were generated.
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kGeneratorBaseName, "_bin_decoder.h")));
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kGeneratorBaseName, "_bin_decoder.cc")));
std::ifstream decoder_file(
absl::StrCat(output_dir, "/", kGeneratorBaseName, "_bin_decoder.cc"));
CHECK(decoder_file.good());
// Verify that decoder entries include the instructions inside the GENERATE()
// construct.
std::string decoder_str((std::istreambuf_iterator<char>(decoder_file)),
(std::istreambuf_iterator<char>()));
for (auto name : {"kBeq", "kBne", "kBlt", "kBltu", "kBge", "kBgeu"}) {
RE2 re(name);
EXPECT_TRUE(RE2::PartialMatch(decoder_str, re)) << name;
}
}
TEST_F(BinFormatVisitorTest, GeneratorErrorDuplicateVariable) {
// Create the file.
std::string file_name =
testing::TempDir() + "/" + "duplicate_variable.bin_fmt";
std::ofstream output_file(file_name, std::ofstream::out);
CHECK(output_file.good());
output_file << kBinFmtPrefix;
// The GENERATE block reuses 'btype' in the range specification.
output_file << R"mid(
GENERATE( [ btype, func3, btype] = [{"eq", 0b000, 1}, {"ne", 0b001, 2},
{lt, 0b100, 3}, {ge, 0b101, 4}, {ltu, 0b110, 5}, {geu, 0b111, 6}]) {
b$(btype) : BType : func3 == $(func3), opcode == 0b110'0011;
};
)mid";
output_file << kBinFmtSuffix;
output_file.close();
std::vector<std::string> input_files = {file_name};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process(input_files, kGeneratorDecoderName,
kGeneratorBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_FALSE(success);
// Make sure the error about duplicate binding variable 'btype' is found.
auto ptr =
log_sink.error_log().find("Duplicate binding variable name 'btype'");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, GeneratorErrorTuplesError) {
// Create the file.
std::string file_name = testing::TempDir() + "/" + "tuples_required.bin_fmt";
std::ofstream output_file(file_name, std::ofstream::out);
CHECK(output_file.good());
output_file << kBinFmtPrefix;
// The GENERATE block adds an extra value in the first tuple.
output_file << R"mid(
GENERATE( [ btype, func3, unused] = [{"eq", 0b000, 1, 3}, {"ne", 0b001, 2},
{lt, 0b100, 3}, {ge, 0b101, 4}, {ltu, 0b110, 5}, {geu, 0b111, 6}]) {
b$(btype) : BType : func3 == $(func3), opcode == 0b110'0011;
};
)mid";
output_file << kBinFmtSuffix;
output_file.close();
std::vector<std::string> input_files = {file_name};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process(input_files, kGeneratorDecoderName,
kGeneratorBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_FALSE(success);
// Make sure the error about duplicate binding variable 'btype' is found.
auto ptr = log_sink.error_log().find(
"Number of values differs from number of identifiers");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, GeneratorErrorUndefinedBindingVariable) {
// Create the file.
std::string file_name =
testing::TempDir() + "/" + "undefined_variable.bin_fmt";
std::ofstream output_file(file_name, std::ofstream::out);
CHECK(output_file.good());
output_file << kBinFmtPrefix;
// The GENERATE block references 'funcX'.
output_file << R"mid(
GENERATE( [ btype, func3] = [{"eq", 0b000}, {"ne", 0b001},
{lt, 0b100}, {ge, 0b101}, {ltu, 0b110}, {geu, 0b111}]) {
b$(btype) : BType : func3 == $(funcX), opcode == 0b110'0011;
};
)mid";
output_file << kBinFmtSuffix;
output_file.close();
std::vector<std::string> input_files = {file_name};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process(input_files, kGeneratorDecoderName,
kGeneratorBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_FALSE(success);
// Make sure the error about duplicate binding variable 'funcX' is found.
auto ptr = log_sink.error_log().find("Undefined binding variable 'funcX'");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, Vliw) {
// Set up input and output file paths.
std::vector<std::string> input_files = {
absl::StrCat(kDepotPath, "testfiles/", kVliwBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process(input_files, kVliwDecoderName, kVliwBaseName,
paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_TRUE(success);
// Verify that the extraction functions for the vliw format were generated.
std::ifstream decoder_file(
absl::StrCat(output_dir, "/", kVliwBaseName, "_bin_decoder.h"));
CHECK(decoder_file.good());
// Verify that decoder entries include extraction functions for the vliw
// format.
std::string decoder_str((std::istreambuf_iterator<char>(decoder_file)),
(std::istreambuf_iterator<char>()));
for (auto name : {"ExtractI0", "ExtractI1", "ExtractI2"}) {
RE2 re(name);
EXPECT_TRUE(RE2::PartialMatch(decoder_str, re)) << name;
}
}
TEST_F(BinFormatVisitorTest, InstructionGroupGrouping) {
// Set up input and output file paths.
std::vector<std::string> input_files = {absl::StrCat(
kDepotPath, "testfiles/", kInstructionGroupBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
// Parse and process the input file, capturing the log.
LogSink log_sink;
absl::AddLogSink(&log_sink);
auto success = visitor
.Process(input_files, kInstructionGroupDecoderName,
kInstructionGroupBaseName, paths_, output_dir)
.ok();
absl::RemoveLogSink(&log_sink);
EXPECT_TRUE(success);
}
TEST_F(BinFormatVisitorTest, SyntaxError) {
std::vector<std::string> input_files = {absl::StrCat(
kDepotPath, "testfiles/", kBinFmtSyntaxErrorBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
LogSink log_sink;
absl::AddLogSink(&log_sink);
EXPECT_FALSE(
visitor.Process(input_files, kDecoderName, kBaseName, paths_, output_dir)
.ok());
absl::RemoveLogSink(&log_sink);
// Verify that there was an error message about mismatched input.
auto ptr = log_sink.error_log().find("mismatched input '");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(" expecting ");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, InstDefFormatError) {
std::vector<std::string> input_files = {absl::StrCat(
kDepotPath, "testfiles/", kBinFmtFormatErrorBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
LogSink log_sink;
absl::AddLogSink(&log_sink);
EXPECT_FALSE(
visitor.Process(input_files, kDecoderName, kBaseName, paths_, output_dir)
.ok());
absl::RemoveLogSink(&log_sink);
// There should be a number of error messages:
// * Multiple definitions of format 'ZFormat'.
// * Multiple definitions of instruction group 'RiscVGInst32'.
// * Multiple definitions of decoder 'ErrorTest'.
// * Field 'rs2' already defined.
// * Overlay 'imm7' already defined as a field.
// * Overlay 'b_imm' reference to 'immX' does not name a field in 'BType'.
// * Overlay 'b_imm' declared width (13) differs from computed width (9).
// * Overlay 'overlay0' already defined as an overlay.
// * Format 'BType' declared width (32) differs from declared width (27).
// * YFormat being of different width than the format it inherits from.
// * XFormat not deriving from Inst32Format.
auto ptr = log_sink.error_log().find(
"Error: Multiple definitions of format 'ZFormat' first defined at ");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Multiple definitions of instruction group 'RiscVGInst32' first "
"defined at");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Multiple definitions of decoder 'ErrorTest' first defined at");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find("Error: Field 'rs2' already defined");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Overlay 'imm7' already defined as a "
"field");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Overlay 'b_imm' reference to 'immX' does not name a field in "
"'BType'");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Overlay 'b_imm' declared width (13) differs from computed width "
"(9)");
ptr = log_sink.error_log().find(
"Error: Overlay 'overlay0' already defined as an "
"overlay");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Format 'BType' declared width (32) differs from computed width "
"(27)");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Format 'XFormat' used by instruction encoding 'none_0' is not "
"derived from");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Format 'YFormat' declared width (36) differs from width "
"inherited from 'Inst32Format' (32)");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Only overlays <= 64 bits are supported for now");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Instruction group 'X': width must be <= 64 bits");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Width of format 'Inst32Format' (32) differs from the declared "
"width of instruction group 'Z' (34)");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Length of format 'Format33' (33) differs from the declared width "
"of the instruction group (32)");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Format 'None' referenced by instruction 'none_2' not defined");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find("Error: ZFormat: illegal use of format name");
EXPECT_TRUE(ptr != std::string::npos);
ptr =
log_sink.error_log().find("Error: More than one opcode enum declaration");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Instruction group 'RiscVGInst32' listed twice");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find("Error: More than one namespace declaration");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find("Error: Parent format 'TypeX' not defined");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Format 'TypeA': must specify a width or inherited format");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, InstDefFormatErrorUndef) {
std::vector<std::string> input_files = {absl::StrCat(
kDepotPath, "testfiles/", kBinFmtFormatErrorUndefBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
LogSink log_sink;
absl::AddLogSink(&log_sink);
EXPECT_FALSE(
visitor.Process(input_files, kDecoderName, kBaseName, paths_, output_dir)
.ok());
absl::RemoveLogSink(&log_sink);
auto ptr = log_sink.error_log().find(
"Error: Undefined format 'NoneSuch' used by instruction group "
"'RiscVGInst32'");
EXPECT_TRUE(ptr != std::string::npos);
}
TEST_F(BinFormatVisitorTest, Constraints) {
std::vector<std::string> input_files = {
absl::StrCat(kDepotPath, "testfiles/", kConstraintsBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
LogSink log_sink;
absl::AddLogSink(&log_sink);
EXPECT_TRUE(visitor
.Process(input_files, kConstraintsDecoderName,
kConstraintsBaseName, paths_, output_dir)
.ok());
absl::RemoveLogSink(&log_sink);
// Verify that the decoder files _bin_decoder.{.h,.cc} files were generated.
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kConstraintsBaseName, "_bin_decoder.h")));
EXPECT_TRUE(FileExists(
absl::StrCat(output_dir, "/", kConstraintsBaseName, "_bin_decoder.cc")));
std::ifstream decoder_file(
absl::StrCat(output_dir, "/", kConstraintsBaseName, "_bin_decoder.cc"));
CHECK(decoder_file.good());
// Verify that decoder entries use the different constraint types.
std::string decoder_str((std::istreambuf_iterator<char>(decoder_file)),
(std::istreambuf_iterator<char>()));
for (auto const& fragment :
{"field1_value != 0x1", "field2_value > 0x2", "field3_value >= 0x3",
"field4_value < 0x4", "field5_value <= 0x5"}) {
RE2 re(fragment);
EXPECT_TRUE(RE2::PartialMatch(decoder_str, re)) << fragment;
}
}
TEST_F(BinFormatVisitorTest, InstructionGroupErrors) {
std::vector<std::string> input_files = {absl::StrCat(
kDepotPath, "testfiles/", kInstructionGroupErrorsBaseName, ".bin_fmt")};
ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
BinFormatVisitor visitor;
LogSink log_sink;
absl::AddLogSink(&log_sink);
EXPECT_FALSE(visitor
.Process(input_files, kInstructionGroupErrorsDecoderName,
kInstructionGroupErrorsBaseName, paths_, output_dir)
.ok());
absl::RemoveLogSink(&log_sink);
auto ptr = log_sink.error_log().find(
"Error: No such instruction group: 'InstGroup'");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Instruction group added twice: 'inst32a' - ignored");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Instruction group 'InstGroup2' listed twice");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Instruction group added twice: 'inst32c' - ignored");
EXPECT_TRUE(ptr != std::string::npos);
ptr =
log_sink.error_log().find("Error: Instruction group 'inst32d' not found");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find(
"Error: Instruction group 'inst32b' must use format 'Inst32Format, to be "
"merged into group 'InstGroup1'");
EXPECT_TRUE(ptr != std::string::npos);
ptr = log_sink.error_log().find("Error: No child groups");
EXPECT_TRUE(ptr != std::string::npos);
}
} // namespace