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