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