1 #include "clangmm.hpp"
2 #include "compile_commands.hpp"
3 #include "meson.hpp"
4 #include "project.hpp"
5 #include "usages_clang.hpp"
6 #include <cassert>
7 #include <fstream>
8 #include <gtksourceviewmm.h>
9
10 //Requires display server to work
11 //However, it is possible to use the Broadway backend if the test is run in a pure terminal environment:
12 //broadwayd&
13 //make test
14
main()15 int main() {
16 auto app = Gtk::Application::create();
17 Gsv::init();
18
19 auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
20 auto project_path = boost::filesystem::canonical(tests_path / "usages_clang_test_files");
21 auto build_path = project_path / "build";
22
23 auto build = Project::Build::create(project_path);
24 g_assert(build->project_path == project_path);
25
26 {
27 auto index = std::make_shared<clangmm::Index>(1, 0);
28 auto path = project_path / "main.cpp";
29 std::ifstream stream(path.string(), std::ifstream::binary);
30 assert(stream);
31 std::string buffer;
32 buffer.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
33 auto arguments = CompileCommands::get_arguments(build_path, path);
34 clangmm::TranslationUnit translation_unit(index, path.string(), arguments, &buffer);
35 auto tokens = translation_unit.get_tokens();
36 clangmm::Token *found_token = nullptr;
37 for(auto &token : *tokens) {
38 if(token.get_spelling() == "a") {
39 found_token = &token;
40 break;
41 }
42 }
43 assert(found_token);
44 auto spelling = found_token->get_spelling();
45 auto cursor = found_token->get_cursor().get_referenced();
46 std::vector<Usages::Clang::Usages> usages;
47 Usages::Clang::PathSet visited;
48
49 Usages::Clang::add_usages(project_path, build_path, boost::filesystem::path(), usages, visited, spelling, cursor, &translation_unit, false);
50 assert(usages.size() == 1);
51 assert(usages[0].path == path);
52 assert(usages[0].lines.size() == 1);
53 assert(usages[0].lines[0] == " test.a = 2;");
54 assert(usages[0].offsets.size() == 1);
55 assert(usages[0].offsets[0].first.line == 6);
56 assert(usages[0].offsets[0].first.index == 8);
57 assert(usages[0].offsets[0].second.line == 6);
58 assert(usages[0].offsets[0].second.index == 9);
59
60 Usages::Clang::add_usages_from_includes(project_path, build_path, usages, visited, spelling, cursor, &translation_unit, false);
61 assert(usages.size() == 2);
62 assert(usages[1].path == project_path / "test.hpp");
63 assert(usages[1].lines.size() == 2);
64 assert(usages[1].lines[0] == " int a = 0;");
65 assert(usages[1].lines[1] == " ++a;");
66 assert(usages[1].offsets.size() == 2);
67 assert(usages[1].offsets[0].first.line == 6);
68 assert(usages[1].offsets[0].first.index == 7);
69 assert(usages[1].offsets[0].second.line == 6);
70 assert(usages[1].offsets[0].second.index == 8);
71 assert(usages[1].offsets[1].first.line == 8);
72 assert(usages[1].offsets[1].first.index == 7);
73 assert(usages[1].offsets[1].second.line == 8);
74 assert(usages[1].offsets[1].second.index == 8);
75
76 auto paths = Usages::Clang::find_paths(project_path, build_path, build_path / "debug");
77 auto pair = Usages::Clang::parse_paths(spelling, paths);
78
79 auto &paths_includes = pair.first;
80 assert(paths_includes.size() == 3);
81 assert(paths_includes.find(project_path / "main.cpp") != paths_includes.end());
82 assert(paths_includes.find(project_path / "test.hpp") != paths_includes.end());
83 assert(paths_includes.find(project_path / "test2.hpp") != paths_includes.end());
84
85 auto &paths_with_spelling = pair.second;
86 assert(paths_with_spelling.size() == 3);
87 assert(paths_with_spelling.find(project_path / "main.cpp") != paths_with_spelling.end());
88 assert(paths_with_spelling.find(project_path / "test.hpp") != paths_with_spelling.end());
89 assert(paths_with_spelling.find(project_path / "test2.hpp") != paths_with_spelling.end());
90
91 auto pair2 = Usages::Clang::find_potential_paths({cursor.get_canonical().get_source_location().get_path()}, project_path, pair.first, pair.second);
92
93 auto &potential_paths = pair2.first;
94 assert(potential_paths.size() == 3);
95 assert(potential_paths.find(project_path / "main.cpp") != potential_paths.end());
96 assert(potential_paths.find(project_path / "test.hpp") != potential_paths.end());
97 assert(potential_paths.find(project_path / "test2.hpp") != potential_paths.end());
98
99 auto &all_includes = pair2.second;
100 assert(all_includes.size() == 1);
101 assert(*all_includes.begin() == project_path / "test.hpp");
102
103 // Remove visited paths
104 for(auto it = potential_paths.begin(); it != potential_paths.end();) {
105 if(visited.find(*it) != visited.end())
106 it = potential_paths.erase(it);
107 else
108 ++it;
109 }
110
111 assert(potential_paths.size() == 1);
112 assert(*potential_paths.begin() == project_path / "test2.hpp");
113
114 {
115 auto path = *potential_paths.begin();
116 std::ifstream stream(path.string(), std::ifstream::binary);
117 std::string buffer;
118 buffer.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
119 clangmm::TranslationUnit translation_unit(index, path.string(), arguments, &buffer);
120 Usages::Clang::add_usages(project_path, build_path, path, usages, visited, spelling, cursor, &translation_unit, true);
121 Usages::Clang::add_usages_from_includes(project_path, build_path, usages, visited, spelling, cursor, &translation_unit, true);
122 assert(usages.size() == 3);
123 assert(usages[2].path == path);
124 assert(usages[2].lines.size() == 1);
125 assert(usages[2].lines[0] == " ++a;");
126 assert(usages[2].offsets.size() == 1);
127 assert(usages[2].offsets[0].first.line == 5);
128 assert(usages[2].offsets[0].first.index == 7);
129 assert(usages[2].offsets[0].second.line == 5);
130 assert(usages[2].offsets[0].second.index == 8);
131 }
132
133 assert(Usages::Clang::caches.size() == 1);
134 auto cache_it = Usages::Clang::caches.begin();
135 assert(cache_it->first == project_path / "test2.hpp");
136 assert(cache_it->second.build_path == build_path);
137 assert(cache_it->second.paths_and_last_write_times.size() == 2);
138 assert(cache_it->second.paths_and_last_write_times.find(project_path / "test2.hpp") != cache_it->second.paths_and_last_write_times.end());
139 assert(cache_it->second.paths_and_last_write_times.find(project_path / "test.hpp") != cache_it->second.paths_and_last_write_times.end());
140 assert(cache_it->second.tokens.size());
141 assert(cache_it->second.cursors.size());
142 {
143 std::vector<Usages::Clang::Usages> usages;
144 Usages::Clang::PathSet visited;
145 Usages::Clang::add_usages_from_cache(cache_it->first, usages, visited, spelling, cursor, cache_it->second);
146 assert(usages.size() == 1);
147 assert(usages[0].path == cache_it->first);
148 assert(usages[0].lines.size() == 1);
149 assert(usages[0].lines[0] == " ++a;");
150 assert(usages[0].offsets.size() == 1);
151 assert(usages[0].offsets[0].first.line == 5);
152 assert(usages[0].offsets[0].first.index == 7);
153 assert(usages[0].offsets[0].second.line == 5);
154 assert(usages[0].offsets[0].second.index == 8);
155 }
156
157 Usages::Clang::erase_unused_caches({project_path});
158 Usages::Clang::cache_in_progress();
159 Usages::Clang::cache(project_path, build_path, path, time(nullptr), {project_path}, &translation_unit, tokens.get());
160 assert(Usages::Clang::caches.size() == 3);
161 assert(Usages::Clang::caches.find(project_path / "main.cpp") != Usages::Clang::caches.end());
162 assert(Usages::Clang::caches.find(project_path / "test.hpp") != Usages::Clang::caches.end());
163 assert(Usages::Clang::caches.find(project_path / "test2.hpp") != Usages::Clang::caches.end());
164
165 Usages::Clang::erase_unused_caches({});
166 Usages::Clang::cache_in_progress();
167 Usages::Clang::cache(project_path, build_path, path, time(nullptr), {}, &translation_unit, tokens.get());
168 assert(Usages::Clang::caches.size() == 0);
169
170 auto cache = Usages::Clang::read_cache(project_path, build_path, project_path / "main.cpp");
171 assert(cache);
172 Usages::Clang::caches.emplace(project_path / "main.cpp", std::move(cache));
173 assert(!cache);
174 assert(Usages::Clang::caches.size() == 1);
175 cache = Usages::Clang::read_cache(project_path, build_path, project_path / "test.hpp");
176 assert(cache);
177 Usages::Clang::caches.emplace(project_path / "test.hpp", std::move(cache));
178 assert(!cache);
179 assert(Usages::Clang::caches.size() == 2);
180 cache = Usages::Clang::read_cache(project_path, build_path, project_path / "test2.hpp");
181 assert(cache);
182 Usages::Clang::caches.emplace(project_path / "test2.hpp", std::move(cache));
183 assert(!cache);
184 assert(Usages::Clang::caches.size() == 3);
185 cache = Usages::Clang::read_cache(project_path, build_path, project_path / "test_not_existing.hpp");
186 assert(!cache);
187 assert(Usages::Clang::caches.size() == 3);
188 {
189 auto cache_it = Usages::Clang::caches.find(project_path / "main.cpp");
190 assert(cache_it != Usages::Clang::caches.end());
191 assert(cache_it->first == project_path / "main.cpp");
192 assert(cache_it->second.build_path == build_path);
193 assert(cache_it->second.paths_and_last_write_times.size() == 2);
194 assert(cache_it->second.paths_and_last_write_times.find(project_path / "main.cpp") != cache_it->second.paths_and_last_write_times.end());
195 assert(cache_it->second.paths_and_last_write_times.find(project_path / "test.hpp") != cache_it->second.paths_and_last_write_times.end());
196 assert(cache_it->second.tokens.size());
197 assert(cache_it->second.cursors.size());
198 {
199 std::vector<Usages::Clang::Usages> usages;
200 Usages::Clang::PathSet visited;
201 Usages::Clang::add_usages_from_cache(cache_it->first, usages, visited, spelling, cursor, cache_it->second);
202 assert(usages.size() == 1);
203 assert(usages[0].path == cache_it->first);
204 assert(usages[0].lines.size() == 1);
205 assert(usages[0].lines[0] == " test.a = 2;");
206 assert(usages[0].offsets.size() == 1);
207 assert(usages[0].offsets[0].first.line == 6);
208 assert(usages[0].offsets[0].first.index == 8);
209 assert(usages[0].offsets[0].second.line == 6);
210 assert(usages[0].offsets[0].second.index == 9);
211 }
212 }
213 {
214 auto cache_it = Usages::Clang::caches.find(project_path / "test.hpp");
215 assert(cache_it != Usages::Clang::caches.end());
216 assert(cache_it->first == project_path / "test.hpp");
217 assert(cache_it->second.build_path == build_path);
218 assert(cache_it->second.paths_and_last_write_times.size() == 1);
219 assert(cache_it->second.paths_and_last_write_times.find(project_path / "test.hpp") != cache_it->second.paths_and_last_write_times.end());
220 assert(cache_it->second.tokens.size());
221 assert(cache_it->second.cursors.size());
222 {
223 std::vector<Usages::Clang::Usages> usages;
224 Usages::Clang::PathSet visited;
225 Usages::Clang::add_usages_from_cache(cache_it->first, usages, visited, spelling, cursor, cache_it->second);
226 assert(usages.size() == 1);
227 assert(usages[0].path == cache_it->first);
228 assert(usages[0].lines.size() == 2);
229 assert(usages[0].lines[0] == " int a = 0;");
230 assert(usages[0].lines[1] == " ++a;");
231 assert(usages[0].offsets.size() == 2);
232 assert(usages[0].offsets[0].first.line == 6);
233 assert(usages[0].offsets[0].first.index == 7);
234 assert(usages[0].offsets[0].second.line == 6);
235 assert(usages[0].offsets[0].second.index == 8);
236 assert(usages[0].offsets[1].first.line == 8);
237 assert(usages[0].offsets[1].first.index == 7);
238 assert(usages[0].offsets[1].second.line == 8);
239 assert(usages[0].offsets[1].second.index == 8);
240 }
241 }
242 {
243 auto cache_it = Usages::Clang::caches.find(project_path / "test2.hpp");
244 assert(cache_it != Usages::Clang::caches.end());
245 assert(cache_it->first == project_path / "test2.hpp");
246 assert(cache_it->second.build_path == build_path);
247 assert(cache_it->second.paths_and_last_write_times.size() == 2);
248 assert(cache_it->second.paths_and_last_write_times.find(project_path / "test2.hpp") != cache_it->second.paths_and_last_write_times.end());
249 assert(cache_it->second.paths_and_last_write_times.find(project_path / "test.hpp") != cache_it->second.paths_and_last_write_times.end());
250 assert(cache_it->second.tokens.size());
251 assert(cache_it->second.cursors.size());
252 {
253 std::vector<Usages::Clang::Usages> usages;
254 Usages::Clang::PathSet visited;
255 Usages::Clang::add_usages_from_cache(cache_it->first, usages, visited, spelling, cursor, cache_it->second);
256 assert(usages.size() == 1);
257 assert(usages[0].path == cache_it->first);
258 assert(usages[0].lines.size() == 1);
259 assert(usages[0].lines[0] == " ++a;");
260 assert(usages[0].offsets.size() == 1);
261 assert(usages[0].offsets[0].first.line == 5);
262 assert(usages[0].offsets[0].first.index == 7);
263 assert(usages[0].offsets[0].second.line == 5);
264 assert(usages[0].offsets[0].second.index == 8);
265 }
266 }
267 }
268 {
269 assert(!Usages::Clang::caches.empty());
270 assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder));
271 assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "main.cpp.usages"));
272 assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test.hpp.usages"));
273 assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test2.hpp.usages"));
274
275 Usages::Clang::erase_all_caches_for_project(project_path, build_path);
276 assert(Usages::Clang::caches.empty());
277 assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder));
278 assert(!boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "main.cpp.usages"));
279 assert(!boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test.hpp.usages"));
280 assert(!boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test2.hpp.usages"));
281 }
282 }
283