| // 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 |