blob: 0adcf96c284c2be7fe3b0808fdedbcf4918243af [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License v2.0 with LLVM Exceptions (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// 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.
//
// Author: Siddharth Nayyar
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>
#include <catch2/catch.hpp>
#include "comparison.h"
#include "fidelity.h"
#include "graph.h"
#include "input.h"
#include "naming.h"
#include "reader_options.h"
#include "reporting.h"
#include "runtime.h"
namespace stg {
namespace {
struct IgnoreTestCase {
const std::string name;
const InputFormat format0;
const std::string file0;
const InputFormat format1;
const std::string file1;
const diff::Ignore ignore;
const std::string expected_output;
const bool expected_same;
};
std::string filename_to_path(const std::string& f) {
return std::filesystem::path("testdata") / f;
}
Id Read(Runtime& runtime, Graph& graph, InputFormat format,
const std::string& input) {
return Read(runtime, graph, format, filename_to_path(input).c_str(),
ReadOptions(), nullptr);
}
TEST_CASE("ignore") {
const auto test = GENERATE(
IgnoreTestCase(
{"symbol type presence change",
InputFormat::ABI,
"symbol_type_presence_0.xml",
InputFormat::ABI,
"symbol_type_presence_1.xml",
diff::Ignore(),
"symbol_type_presence_small_diff",
false}),
IgnoreTestCase(
{"symbol type presence change pruned",
InputFormat::ABI,
"symbol_type_presence_0.xml",
InputFormat::ABI,
"symbol_type_presence_1.xml",
diff::Ignore(diff::Ignore::SYMBOL_TYPE_PRESENCE),
"empty",
true}),
IgnoreTestCase(
{"type declaration status change",
InputFormat::ABI,
"type_declaration_status_0.xml",
InputFormat::ABI,
"type_declaration_status_1.xml",
diff::Ignore(),
"type_declaration_status_small_diff",
false}),
IgnoreTestCase(
{"type declaration status change pruned",
InputFormat::ABI,
"type_declaration_status_0.xml",
InputFormat::ABI,
"type_declaration_status_1.xml",
diff::Ignore(diff::Ignore::TYPE_DECLARATION_STATUS),
"empty",
true}),
IgnoreTestCase(
{"primitive type encoding",
InputFormat::STG,
"primitive_type_encoding_0.stg",
InputFormat::STG,
"primitive_type_encoding_1.stg",
diff::Ignore(),
"primitive_type_encoding_small_diff",
false}),
IgnoreTestCase(
{"primitive type encoding ignored",
InputFormat::STG,
"primitive_type_encoding_0.stg",
InputFormat::STG,
"primitive_type_encoding_1.stg",
diff::Ignore(diff::Ignore::PRIMITIVE_TYPE_ENCODING),
"empty",
true}),
IgnoreTestCase(
{"member size",
InputFormat::STG,
"member_size_0.stg",
InputFormat::STG,
"member_size_1.stg",
diff::Ignore(),
"member_size_small_diff",
false}),
IgnoreTestCase(
{"member size ignored",
InputFormat::STG,
"member_size_0.stg",
InputFormat::STG,
"member_size_1.stg",
diff::Ignore(diff::Ignore::MEMBER_SIZE),
"empty",
true}),
IgnoreTestCase(
{"enum underlying type",
InputFormat::STG,
"enum_underlying_type_0.stg",
InputFormat::STG,
"enum_underlying_type_1.stg",
diff::Ignore(),
"enum_underlying_type_small_diff",
false}),
IgnoreTestCase(
{"enum underlying type ignored",
InputFormat::STG,
"enum_underlying_type_0.stg",
InputFormat::STG,
"enum_underlying_type_1.stg",
diff::Ignore(diff::Ignore::ENUM_UNDERLYING_TYPE),
"empty",
true}),
IgnoreTestCase(
{"qualifier",
InputFormat::STG,
"qualifier_0.stg",
InputFormat::STG,
"qualifier_1.stg",
diff::Ignore(),
"qualifier_small_diff",
false}),
IgnoreTestCase(
{"qualifier ignored",
InputFormat::STG,
"qualifier_0.stg",
InputFormat::STG,
"qualifier_1.stg",
diff::Ignore(diff::Ignore::QUALIFIER),
"empty",
true}),
IgnoreTestCase(
{"CRC change",
InputFormat::STG,
"crc_change_0.stg",
InputFormat::STG,
"crc_change_1.stg",
diff::Ignore(),
"crc_change_small_diff",
false}),
IgnoreTestCase(
{"CRC change ignored",
InputFormat::STG,
"crc_change_0.stg",
InputFormat::STG,
"crc_change_1.stg",
diff::Ignore(diff::Ignore::SYMBOL_CRC),
"empty",
true}),
IgnoreTestCase(
{"interface addition",
InputFormat::STG,
"interface_addition_0.stg",
InputFormat::STG,
"interface_addition_1.stg",
diff::Ignore(),
"interface_addition_small_diff",
false}),
IgnoreTestCase(
{"interface addition ignored",
InputFormat::STG,
"interface_addition_0.stg",
InputFormat::STG,
"interface_addition_1.stg",
diff::Ignore(diff::Ignore::INTERFACE_ADDITION),
"empty",
true}),
IgnoreTestCase(
{"type addition",
InputFormat::STG,
"type_addition_0.stg",
InputFormat::STG,
"type_addition_1.stg",
diff::Ignore(),
"type_addition_small_diff",
false}),
IgnoreTestCase(
{"type addition ignored",
InputFormat::STG,
"type_addition_0.stg",
InputFormat::STG,
"type_addition_1.stg",
diff::Ignore(diff::Ignore::INTERFACE_ADDITION),
"empty",
true}),
IgnoreTestCase(
{"type definition addition",
InputFormat::STG,
"type_addition_1.stg",
InputFormat::STG,
"type_addition_2.stg",
diff::Ignore(),
"type_definition_addition_small_diff",
false}),
IgnoreTestCase(
{"type definition addition ignored",
InputFormat::STG,
"type_addition_1.stg",
InputFormat::STG,
"type_addition_2.stg",
diff::Ignore(diff::Ignore::TYPE_DEFINITION_ADDITION),
"empty",
true})
);
SECTION(test.name) {
std::ostringstream os;
Runtime runtime(os, false);
// Read inputs.
Graph graph;
const Id id0 = Read(runtime, graph, test.format0, test.file0);
const Id id1 = Read(runtime, graph, test.format1, test.file1);
// Compute differences.
stg::diff::Outcomes outcomes;
const auto comparison =
diff::Compare(runtime, test.ignore, graph, id0, id1, outcomes);
const bool same = comparison == diff::Comparison{};
// Write SMALL reports.
std::ostringstream output;
if (!same) {
NameCache names;
const reporting::Options options{reporting::OutputFormat::SMALL};
const reporting::Reporting reporting{graph, outcomes, options, names};
Report(reporting, comparison, output);
}
// Check comparison outcome and report output.
CHECK(same == test.expected_same);
const std::ifstream expected_output_file(
filename_to_path(test.expected_output));
std::ostringstream expected_output;
expected_output << expected_output_file.rdbuf();
CHECK(output.str() == expected_output.str());
}
}
struct ShortReportTestCase {
const std::string name;
InputFormat format;
const std::string file0;
const std::string file1;
const std::string expected_output;
};
TEST_CASE("short report") {
const auto test = GENERATE(
ShortReportTestCase(
{"crc changes", InputFormat::ABI, "crc_0.xml", "crc_1.xml",
"crc_changes_short_diff"}),
ShortReportTestCase(
{"only crc changes", InputFormat::ABI, "crc_only_0.xml",
"crc_only_1.xml", "crc_only_changes_short_diff"}),
ShortReportTestCase(
{"offset changes", InputFormat::ABI, "offset_0.xml",
"offset_1.xml", "offset_changes_short_diff"}),
ShortReportTestCase(
{"symbols added and removed", InputFormat::ABI,
"added_removed_symbols_0.xml", "added_removed_symbols_1.xml",
"added_removed_symbols_short_diff"}),
ShortReportTestCase(
{"symbols added and removed only", InputFormat::ABI,
"added_removed_symbols_only_0.xml",
"added_removed_symbols_only_1.xml",
"added_removed_symbols_only_short_diff"}),
ShortReportTestCase(
{"enumerators added and removed", stg::InputFormat::STG,
"added_removed_enumerators_0.stg",
"added_removed_enumerators_1.stg",
"added_removed_enumerators_short_diff"}));
SECTION(test.name) {
std::ostringstream os;
Runtime runtime(os, false);
// Read inputs.
Graph graph;
const Id id0 = Read(runtime, graph, test.format, test.file0);
const Id id1 = Read(runtime, graph, test.format, test.file1);
// Compute differences.
stg::diff::Outcomes outcomes;
const auto comparison =
diff::Compare(runtime, {}, graph, id0, id1, outcomes);
const bool same = comparison == diff::Comparison{};
// Write SHORT reports.
std::stringstream output;
if (!same) {
NameCache names;
const reporting::Options options{reporting::OutputFormat::SHORT};
const reporting::Reporting reporting{graph, outcomes, options, names};
Report(reporting, comparison, output);
}
// Check comparison outcome and report output.
CHECK(!same);
const std::ifstream expected_output_file(
filename_to_path(test.expected_output));
std::ostringstream expected_output;
expected_output << expected_output_file.rdbuf();
CHECK(output.str() == expected_output.str());
}
}
TEST_CASE("fidelity diff") {
std::ostringstream os;
Runtime runtime(os, false);
// Read inputs.
Graph graph;
const Id id0 = Read(runtime, graph, InputFormat::STG, "fidelity_diff_0.stg");
const Id id1 = Read(runtime, graph, InputFormat::STG, "fidelity_diff_1.stg");
// Compute fidelity diff.
auto fidelity_diff = GetFidelityTransitions(graph, id0, id1);
// Write fidelity diff report.
std::ostringstream report;
reporting::FidelityDiff(fidelity_diff, report);
// Check report.
const std::ifstream expected_report_file(
filename_to_path("fidelity_diff_report"));
std::ostringstream expected_report;
expected_report << expected_report_file.rdbuf();
CHECK(report.str() == expected_report.str());
}
} // namespace
} // namespace stg