1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2023 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License. You may obtain a copy of the License at
9 //
10 // https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Giuliano Procida
19
20 #include <cstddef>
21 #include <filesystem>
22 #include <iostream>
23 #include <optional>
24 #include <vector>
25
26 #include <catch2/catch.hpp>
27 #include <libxml/tree.h>
28 #include "abigail_reader.h"
29 #include "equality.h"
30 #include "graph.h"
31 #include "runtime.h"
32
33 namespace {
34
filename_to_path(const char * f)35 std::filesystem::path filename_to_path(const char* f) {
36 return std::filesystem::path("testdata") / f;
37 }
38
Read(const char * input)39 stg::abixml::Document Read(const char* input) {
40 stg::Runtime runtime(std::cerr, false);
41 return stg::abixml::Read(runtime, filename_to_path(input));
42 }
43
Read(stg::Graph & graph,const char * input)44 stg::Id Read(stg::Graph& graph, const char* input) {
45 stg::Runtime runtime(std::cerr, false);
46 return stg::abixml::Read(runtime, graph, filename_to_path(input));
47 }
48
49 struct EqualTreeTestCase {
50 const char* name;
51 const char* left;
52 const char* right;
53 bool equal;
54 };
55
56 TEST_CASE("EqualTree") {
57 const auto test = GENERATE(
58 EqualTreeTestCase(
59 {"cleaning",
60 "abigail_dirty.xml",
61 "abigail_clean.xml",
62 true}),
63 EqualTreeTestCase(
64 {"self comparison",
65 "abigail_tree_0.xml",
66 "abigail_tree_0.xml",
67 true}),
68 EqualTreeTestCase(
69 {"attribute order is irrelevant",
70 "abigail_tree_0.xml",
71 "abigail_tree_1.xml",
72 true}),
73 EqualTreeTestCase(
74 {"element order is relevant",
75 "abigail_tree_0.xml",
76 "abigail_tree_2.xml",
77 false}),
78 EqualTreeTestCase(
79 {"attribute missing",
80 "abigail_tree_0.xml",
81 "abigail_tree_3.xml",
82 false}),
83 EqualTreeTestCase(
84 {"element missing",
85 "abigail_tree_0.xml",
86 "abigail_tree_4.xml",
87 false}),
88 EqualTreeTestCase(
89 {"attribute changed",
90 "abigail_tree_0.xml",
91 "abigail_tree_5.xml",
92 false}),
93 EqualTreeTestCase(
94 {"element changed",
95 "abigail_tree_0.xml",
96 "abigail_tree_6.xml",
97 false}));
98
99 SECTION(test.name) {
100 const stg::abixml::Document left_document = Read(test.left);
101 const stg::abixml::Document right_document = Read(test.right);
102 xmlNodePtr left_root = xmlDocGetRootElement(left_document.get());
103 xmlNodePtr right_root = xmlDocGetRootElement(right_document.get());
104 stg::abixml::Clean(left_root);
105 stg::abixml::Clean(right_root);
106 CHECK(stg::abixml::EqualTree(left_root, right_root) == test.equal);
107 CHECK(stg::abixml::EqualTree(right_root, left_root) == test.equal);
108 }
109 }
110
111 struct SubTreeTestCase {
112 const char* name;
113 const char* left;
114 const char* right;
115 bool left_sub_right;
116 bool right_sub_left;
117 };
118
119 TEST_CASE("SubTree") {
120 const auto test = GENERATE(
121 SubTreeTestCase(
122 {"self comparison",
123 "abigail_tree_0.xml",
124 "abigail_tree_0.xml",
125 true, true}),
126 SubTreeTestCase(
127 {"attribute missing",
128 "abigail_tree_0.xml",
129 "abigail_tree_3.xml",
130 false, true}),
131 SubTreeTestCase(
132 {"element missing",
133 "abigail_tree_0.xml",
134 "abigail_tree_4.xml",
135 false, true}),
136 SubTreeTestCase(
137 {"member-type access special case",
138 "abigail_tree_0.xml",
139 "abigail_tree_7.xml",
140 true, true}));
141
142 SECTION(test.name) {
143 const stg::abixml::Document left_document = Read(test.left);
144 const stg::abixml::Document right_document = Read(test.right);
145 xmlNodePtr left_root = xmlDocGetRootElement(left_document.get());
146 xmlNodePtr right_root = xmlDocGetRootElement(right_document.get());
147 stg::abixml::Clean(left_root);
148 stg::abixml::Clean(right_root);
149 CHECK(stg::abixml::SubTree(left_root, right_root) == test.left_sub_right);
150 CHECK(stg::abixml::SubTree(right_root, left_root) == test.right_sub_left);
151 }
152 }
153
154 struct TidyTestCase {
155 const char* name;
156 const std::vector<const char*> files;
157 };
158
159 TEST_CASE("Tidy") {
160 const auto test = GENERATE(
161 TidyTestCase(
162 {"bad DWARF ELF link",
163 {"abigail_bad_dwarf_elf_link_0.xml",
164 "abigail_bad_dwarf_elf_link_1.xml",
165 "abigail_bad_dwarf_elf_link_2.xml"}}),
166 TidyTestCase(
167 {"anonymous type normalisation",
168 {"abigail_anonymous_types_0.xml",
169 "abigail_anonymous_types_1.xml",
170 "abigail_anonymous_types_2.xml",
171 "abigail_anonymous_types_3.xml",
172 "abigail_anonymous_types_4.xml"}}),
173 TidyTestCase(
174 {"duplicate data members",
175 {"abigail_duplicate_data_members_0.xml",
176 "abigail_duplicate_data_members_1.xml"}}),
177 TidyTestCase(
178 {"duplicate type resolution - exact duplicate",
179 {"abigail_duplicate_types_0.xml",
180 "abigail_duplicate_types_1.xml"}}),
181 TidyTestCase(
182 {"duplicate type resolution - partial duplicate",
183 {"abigail_duplicate_types_0.xml",
184 "abigail_duplicate_types_2.xml"}}),
185 TidyTestCase(
186 {"duplicate type resolution - multiple partial duplicates",
187 {"abigail_duplicate_types_0.xml",
188 "abigail_duplicate_types_3.xml"}}),
189 TidyTestCase(
190 {"duplicate type resolution - no maximal duplicate",
191 {"abigail_duplicate_types_4.xml",
192 "abigail_duplicate_types_5.xml"}}),
193 TidyTestCase(
194 {"duplicate type resolution - different scopes",
195 {"abigail_duplicate_types_4.xml",
196 "abigail_duplicate_types_6.xml"}}),
197 TidyTestCase(
198 {"duplicate type resolution - stray anonymous member",
199 {"abigail_duplicate_types_7.xml",
200 "abigail_duplicate_types_8.xml"}}),
201 TidyTestCase(
202 {"corpus group handling",
203 {"abigail_duplicate_types_0.xml",
204 "abigail_duplicate_types_9.xml"}}));
205
206 SECTION(test.name) {
207 // Read inputs.
208 stg::Graph graph;
209 std::vector<stg::Id> ids;
210 ids.reserve(test.files.size());
211 for (const char* file : test.files) {
212 ids.push_back(Read(graph, file));
213 }
214
215 // Useless equality cache.
216 struct NoCache {
Query__anon5a9898f20111::NoCache217 static std::optional<bool> Query(const stg::Pair&) {
218 return std::nullopt;
219 }
AllSame__anon5a9898f20111::NoCache220 void AllSame(const std::vector<stg::Pair>&) {}
AllDifferent__anon5a9898f20111::NoCache221 void AllDifferent(const std::vector<stg::Pair>&) {}
222 };
223
224 // Check exact equality.
225 NoCache cache;
226 for (size_t ix = 1; ix < ids.size(); ++ix) {
227 CHECK(stg::Equals<NoCache>(graph, cache)(ids[0], ids[ix]));
228 }
229 }
230 }
231
232 } // namespace
233