blob: a09a8684fb7e902bacce5a179e3408584de35a8c [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 <iostream>
#include <ostream>
#include <sstream>
#include <string>
#include <catch2/catch.hpp>
#include "comparison.h"
#include "graph.h"
#include "input.h"
#include "metrics.h"
#include "reader_options.h"
#include "reporting.h"
namespace {
struct IgnoreTestCase {
const std::string name;
const stg::InputFormat format0;
const std::string file0;
const stg::InputFormat format1;
const std::string file1;
const stg::Ignore ignore;
const std::string expected_output;
const bool expected_equals;
};
std::string filename_to_path(const std::string& f) {
return std::filesystem::path("testdata") / f;
}
stg::Id Read(stg::Graph& graph, stg::InputFormat format,
const std::string& input, stg::Metrics& metrics) {
return stg::Read(graph, format, filename_to_path(input).c_str(),
stg::ReadOptions(), nullptr, metrics);
}
TEST_CASE("ignore") {
const auto test = GENERATE(
IgnoreTestCase(
{"symbol type presence change",
stg::InputFormat::ABI,
"symbol_type_presence_0.xml",
stg::InputFormat::ABI,
"symbol_type_presence_1.xml",
stg::Ignore(),
"symbol_type_presence_small_diff",
false}),
IgnoreTestCase(
{"symbol type presence change pruned",
stg::InputFormat::ABI,
"symbol_type_presence_0.xml",
stg::InputFormat::ABI,
"symbol_type_presence_1.xml",
stg::Ignore(stg::Ignore::SYMBOL_TYPE_PRESENCE),
"empty",
true}),
IgnoreTestCase(
{"type declaration status change",
stg::InputFormat::ABI,
"type_declaration_status_0.xml",
stg::InputFormat::ABI,
"type_declaration_status_1.xml",
stg::Ignore(),
"type_declaration_status_small_diff",
false}),
IgnoreTestCase(
{"type declaration status change pruned",
stg::InputFormat::ABI,
"type_declaration_status_0.xml",
stg::InputFormat::ABI,
"type_declaration_status_1.xml",
stg::Ignore(stg::Ignore::TYPE_DECLARATION_STATUS),
"empty",
true}),
IgnoreTestCase(
{"primitive type encoding",
stg::InputFormat::STG,
"primitive_type_encoding_0.stg",
stg::InputFormat::STG,
"primitive_type_encoding_1.stg",
stg::Ignore(),
"primitive_type_encoding_small_diff",
false}),
IgnoreTestCase(
{"primitive type encoding ignored",
stg::InputFormat::STG,
"primitive_type_encoding_0.stg",
stg::InputFormat::STG,
"primitive_type_encoding_1.stg",
stg::Ignore(stg::Ignore::PRIMITIVE_TYPE_ENCODING),
"empty",
true}),
IgnoreTestCase(
{"member size",
stg::InputFormat::STG,
"member_size_0.stg",
stg::InputFormat::STG,
"member_size_1.stg",
stg::Ignore(),
"member_size_small_diff",
false}),
IgnoreTestCase(
{"member size ignored",
stg::InputFormat::STG,
"member_size_0.stg",
stg::InputFormat::STG,
"member_size_1.stg",
stg::Ignore(stg::Ignore::MEMBER_SIZE),
"empty",
true}),
IgnoreTestCase(
{"enum underlying type",
stg::InputFormat::STG,
"enum_underlying_type_0.stg",
stg::InputFormat::STG,
"enum_underlying_type_1.stg",
stg::Ignore(),
"enum_underlying_type_small_diff",
false}),
IgnoreTestCase(
{"enum underlying type ignored",
stg::InputFormat::STG,
"enum_underlying_type_0.stg",
stg::InputFormat::STG,
"enum_underlying_type_1.stg",
stg::Ignore(stg::Ignore::ENUM_UNDERLYING_TYPE),
"empty",
true}),
IgnoreTestCase(
{"qualifier",
stg::InputFormat::STG,
"qualifier_0.stg",
stg::InputFormat::STG,
"qualifier_1.stg",
stg::Ignore(),
"qualifier_small_diff",
false}),
IgnoreTestCase(
{"qualifier ignored",
stg::InputFormat::STG,
"qualifier_0.stg",
stg::InputFormat::STG,
"qualifier_1.stg",
stg::Ignore(stg::Ignore::QUALIFIER),
"empty",
true}),
IgnoreTestCase(
{"CRC change",
stg::InputFormat::STG,
"crc_change_0.stg",
stg::InputFormat::STG,
"crc_change_1.stg",
stg::Ignore(),
"crc_change_small_diff",
false}),
IgnoreTestCase(
{"CRC change ignored",
stg::InputFormat::STG,
"crc_change_0.stg",
stg::InputFormat::STG,
"crc_change_1.stg",
stg::Ignore(stg::Ignore::SYMBOL_CRC),
"empty",
true}),
IgnoreTestCase(
{"interface addition",
stg::InputFormat::STG,
"interface_addition_0.stg",
stg::InputFormat::STG,
"interface_addition_1.stg",
stg::Ignore(),
"interface_addition_small_diff",
false}),
IgnoreTestCase(
{"interface addition ignored",
stg::InputFormat::STG,
"interface_addition_0.stg",
stg::InputFormat::STG,
"interface_addition_1.stg",
stg::Ignore(stg::Ignore::INTERFACE_ADDITION),
"empty",
true}),
IgnoreTestCase(
{"type addition",
stg::InputFormat::STG,
"type_addition_0.stg",
stg::InputFormat::STG,
"type_addition_1.stg",
stg::Ignore(),
"type_addition_small_diff",
false}),
IgnoreTestCase(
{"type addition ignored",
stg::InputFormat::STG,
"type_addition_0.stg",
stg::InputFormat::STG,
"type_addition_1.stg",
stg::Ignore(stg::Ignore::INTERFACE_ADDITION),
"empty",
true}),
IgnoreTestCase(
{"type definition addition",
stg::InputFormat::STG,
"type_addition_1.stg",
stg::InputFormat::STG,
"type_addition_2.stg",
stg::Ignore(),
"type_definition_addition_small_diff",
false}),
IgnoreTestCase(
{"type definition addition ignored",
stg::InputFormat::STG,
"type_addition_1.stg",
stg::InputFormat::STG,
"type_addition_2.stg",
stg::Ignore(stg::Ignore::TYPE_DEFINITION_ADDITION),
"empty",
true})
);
SECTION(test.name) {
stg::Metrics metrics;
// Read inputs.
stg::Graph graph;
const auto id0 = Read(graph, test.format0, test.file0, metrics);
const auto id1 = Read(graph, test.format1, test.file1, metrics);
// Compute differences.
stg::Compare compare{graph, test.ignore, metrics};
const auto& [equals, comparison] = compare(id0, id1);
// Write SMALL reports.
std::ostringstream output;
if (comparison) {
stg::NameCache names;
stg::reporting::Options options{stg::reporting::OutputFormat::SMALL, 0};
stg::reporting::Reporting reporting{graph, compare.outcomes, options,
names};
Report(reporting, *comparison, output);
}
// Check comparison outcome and report output.
CHECK(equals == test.expected_equals);
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;
const std::string xml0;
const std::string xml1;
const std::string expected_output;
};
TEST_CASE("short report") {
const auto test = GENERATE(
ShortReportTestCase(
{"crc changes", "crc_0.xml", "crc_1.xml", "crc_changes_short_diff"}),
ShortReportTestCase({"only crc changes", "crc_only_0.xml",
"crc_only_1.xml", "crc_only_changes_short_diff"}),
ShortReportTestCase({"offset changes", "offset_0.xml", "offset_1.xml",
"offset_changes_short_diff"}),
ShortReportTestCase(
{"symbols added and removed", "added_removed_symbols_0.xml",
"added_removed_symbols_1.xml", "added_removed_symbols_short_diff"}),
ShortReportTestCase({"symbols added and removed only",
"added_removed_symbols_only_0.xml",
"added_removed_symbols_only_1.xml",
"added_removed_symbols_only_short_diff"}));
SECTION(test.name) {
stg::Metrics metrics;
// Read inputs.
stg::Graph graph;
const auto id0 = Read(graph, stg::InputFormat::ABI, test.xml0, metrics);
const auto id1 = Read(graph, stg::InputFormat::ABI, test.xml1, metrics);
// Compute differences.
stg::Compare compare{graph, {}, metrics};
const auto& [equals, comparison] = compare(id0, id1);
// Write SHORT reports.
std::stringstream output;
if (comparison) {
stg::NameCache names;
stg::reporting::Options options{stg::reporting::OutputFormat::SHORT, 2};
stg::reporting::Reporting reporting{graph, compare.outcomes, options,
names};
Report(reporting, *comparison, output);
}
// Check comparison outcome and report output.
CHECK(equals == false);
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") {
stg::Metrics metrics;
// Read inputs.
stg::Graph graph;
const auto id0 =
Read(graph, stg::InputFormat::STG, "fidelity_diff_0.stg", metrics);
const auto id1 =
Read(graph, stg::InputFormat::STG, "fidelity_diff_1.stg", metrics);
// Compute fidelity diff.
auto fidelity_diff = stg::GetFidelityTransitions(graph, id0, id1);
// Write fidelity diff report.
std::ostringstream report;
stg::reporting::FidelityDiff(fidelity_diff, report);
// Check report.
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