| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // -*- mode: C++ -*- |
| // |
| // Copyright 2023 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: Giuliano Procida |
| |
| #include <cstddef> |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| #include <optional> |
| #include <ostream> |
| #include <sstream> |
| #include <vector> |
| |
| #include <catch2/catch.hpp> |
| #include "abigail_reader.h" |
| #include "graph.h" |
| #include "metrics.h" |
| #include "equality.h" |
| |
| namespace { |
| |
| std::filesystem::path filename_to_path(const char* f) { |
| return std::filesystem::path("testdata") / f; |
| } |
| |
| stg::abixml::Document Read(const char* input) { |
| stg::Metrics metrics; |
| return stg::abixml::Read(filename_to_path(input), metrics); |
| } |
| |
| stg::Id Read(stg::Graph& graph, const char* input) { |
| stg::Metrics metrics; |
| return stg::abixml::Read(graph, filename_to_path(input), metrics); |
| } |
| |
| struct EqualTreeTestCase { |
| const char* name; |
| const char* left; |
| const char* right; |
| bool equal; |
| }; |
| |
| TEST_CASE("EqualTree") { |
| const auto test = GENERATE( |
| EqualTreeTestCase( |
| {"cleaning", |
| "abigail_dirty.xml", |
| "abigail_clean.xml", |
| true}), |
| EqualTreeTestCase( |
| {"self comparison", |
| "abigail_tree_0.xml", |
| "abigail_tree_0.xml", |
| true}), |
| EqualTreeTestCase( |
| {"attribute order is irrelevant", |
| "abigail_tree_0.xml", |
| "abigail_tree_1.xml", |
| true}), |
| EqualTreeTestCase( |
| {"element order is relevant", |
| "abigail_tree_0.xml", |
| "abigail_tree_2.xml", |
| false}), |
| EqualTreeTestCase( |
| {"attribute missing", |
| "abigail_tree_0.xml", |
| "abigail_tree_3.xml", |
| false}), |
| EqualTreeTestCase( |
| {"element missing", |
| "abigail_tree_0.xml", |
| "abigail_tree_4.xml", |
| false}), |
| EqualTreeTestCase( |
| {"attribute changed", |
| "abigail_tree_0.xml", |
| "abigail_tree_5.xml", |
| false}), |
| EqualTreeTestCase( |
| {"element changed", |
| "abigail_tree_0.xml", |
| "abigail_tree_6.xml", |
| false})); |
| |
| SECTION(test.name) { |
| const stg::abixml::Document left_document = Read(test.left); |
| const stg::abixml::Document right_document = Read(test.right); |
| xmlNodePtr left_root = xmlDocGetRootElement(left_document.get()); |
| xmlNodePtr right_root = xmlDocGetRootElement(right_document.get()); |
| stg::abixml::Clean(left_root); |
| stg::abixml::Clean(right_root); |
| CHECK(stg::abixml::EqualTree(left_root, right_root) == test.equal); |
| CHECK(stg::abixml::EqualTree(right_root, left_root) == test.equal); |
| } |
| } |
| |
| struct SubTreeTestCase { |
| const char* name; |
| const char* left; |
| const char* right; |
| bool left_sub_right; |
| bool right_sub_left; |
| }; |
| |
| TEST_CASE("SubTree") { |
| const auto test = GENERATE( |
| SubTreeTestCase( |
| {"self comparison", |
| "abigail_tree_0.xml", |
| "abigail_tree_0.xml", |
| true, true}), |
| SubTreeTestCase( |
| {"attribute missing", |
| "abigail_tree_0.xml", |
| "abigail_tree_3.xml", |
| false, true}), |
| SubTreeTestCase( |
| {"element missing", |
| "abigail_tree_0.xml", |
| "abigail_tree_4.xml", |
| false, true}), |
| SubTreeTestCase( |
| {"member-type access special case", |
| "abigail_tree_0.xml", |
| "abigail_tree_7.xml", |
| true, true})); |
| |
| SECTION(test.name) { |
| const stg::abixml::Document left_document = Read(test.left); |
| const stg::abixml::Document right_document = Read(test.right); |
| xmlNodePtr left_root = xmlDocGetRootElement(left_document.get()); |
| xmlNodePtr right_root = xmlDocGetRootElement(right_document.get()); |
| stg::abixml::Clean(left_root); |
| stg::abixml::Clean(right_root); |
| CHECK(stg::abixml::SubTree(left_root, right_root) == test.left_sub_right); |
| CHECK(stg::abixml::SubTree(right_root, left_root) == test.right_sub_left); |
| } |
| } |
| |
| struct TidyTestCase { |
| const char* name; |
| const std::vector<const char*> files; |
| }; |
| |
| TEST_CASE("Tidy") { |
| const auto test = GENERATE( |
| TidyTestCase( |
| {"bad DWARF ELF link", |
| {"abigail_bad_dwarf_elf_link_0.xml", |
| "abigail_bad_dwarf_elf_link_1.xml", |
| "abigail_bad_dwarf_elf_link_2.xml"}}), |
| TidyTestCase( |
| {"anonymous type normalisation", |
| {"abigail_anonymous_types_0.xml", |
| "abigail_anonymous_types_1.xml", |
| "abigail_anonymous_types_2.xml", |
| "abigail_anonymous_types_3.xml", |
| "abigail_anonymous_types_4.xml"}}), |
| TidyTestCase( |
| {"duplicate data members", |
| {"abigail_duplicate_data_members_0.xml", |
| "abigail_duplicate_data_members_1.xml"}}), |
| TidyTestCase( |
| {"duplicate type resolution - exact duplicate", |
| {"abigail_duplicate_types_0.xml", |
| "abigail_duplicate_types_1.xml"}}), |
| TidyTestCase( |
| {"duplicate type resolution - partial duplicate", |
| {"abigail_duplicate_types_0.xml", |
| "abigail_duplicate_types_2.xml"}}), |
| TidyTestCase( |
| {"duplicate type resolution - multiple partial duplicates", |
| {"abigail_duplicate_types_0.xml", |
| "abigail_duplicate_types_3.xml"}}), |
| TidyTestCase( |
| {"duplicate type resolution - no maximal duplicate", |
| {"abigail_duplicate_types_4.xml", |
| "abigail_duplicate_types_5.xml"}}), |
| TidyTestCase( |
| {"duplicate type resolution - different scopes", |
| {"abigail_duplicate_types_4.xml", |
| "abigail_duplicate_types_6.xml"}}), |
| TidyTestCase( |
| {"duplicate type resolution - stray anonymous member", |
| {"abigail_duplicate_types_7.xml", |
| "abigail_duplicate_types_8.xml"}})); |
| |
| SECTION(test.name) { |
| // Read inputs. |
| stg::Graph graph; |
| std::vector<stg::Id> ids; |
| ids.reserve(test.files.size()); |
| for (const char* file : test.files) { |
| ids.push_back(Read(graph, file)); |
| } |
| |
| // Useless equality cache. |
| struct NoCache { |
| static std::optional<bool> Query(const stg::Pair&) { |
| return std::nullopt; |
| } |
| void AllSame(const std::vector<stg::Pair>&) {} |
| void AllDifferent(const std::vector<stg::Pair>&) {} |
| }; |
| |
| // Check exact equality. |
| NoCache cache; |
| for (size_t ix = 1; ix < ids.size(); ++ix) { |
| CHECK(stg::Equals<NoCache>(graph, cache)(ids[0], ids[ix])); |
| } |
| } |
| } |
| |
| } // namespace |