|
25 | 25 | #include "helpers.h"
|
26 | 26 | #include "path.h"
|
27 | 27 | #include "preprocessor.h"
|
| 28 | +#include "redirect.h" |
28 | 29 | #include "settings.h"
|
29 | 30 | #include "standards.h"
|
30 | 31 | #include "suppressions.h"
|
31 | 32 |
|
32 | 33 | #include <algorithm>
|
33 | 34 | #include <cstddef>
|
| 35 | +#include <cstdlib> |
34 | 36 | #include <list>
|
35 | 37 | #include <sstream>
|
36 | 38 | #include <string>
|
| 39 | +#include <unordered_set> |
| 40 | +#include <utility> |
37 | 41 | #include <vector>
|
38 | 42 |
|
39 | 43 | #include <simplecpp.h>
|
@@ -66,7 +70,11 @@ class TestCppcheck : public TestFixture {
|
66 | 70 | void run() override {
|
67 | 71 | TEST_CASE(getErrorMessages);
|
68 | 72 | TEST_CASE(checkWithFile);
|
| 73 | + TEST_CASE(checkWithFileWithTools); |
| 74 | + TEST_CASE(checkWithFileWithToolsNoCommand); |
69 | 75 | TEST_CASE(checkWithFS);
|
| 76 | + TEST_CASE(checkWithFSWithTools); |
| 77 | + TEST_CASE(checkWithFSWithToolsNoCommand); |
70 | 78 | TEST_CASE(suppress_error_library);
|
71 | 79 | TEST_CASE(unique_errors);
|
72 | 80 | TEST_CASE(unique_errors_2);
|
@@ -114,49 +122,217 @@ class TestCppcheck : public TestFixture {
|
114 | 122 | ASSERT(foundMissingIncludeSystem);
|
115 | 123 | }
|
116 | 124 |
|
117 |
| - void checkWithFile() const |
| 125 | + static std::string exename(std::string exe) |
118 | 126 | {
|
| 127 | +#ifdef _WIN32 |
| 128 | + return exe + ".exe"; |
| 129 | +#else |
| 130 | + return exe; |
| 131 | +#endif |
| 132 | + } |
| 133 | + |
| 134 | + CppCheck::ExecuteCmdFn getExecuteCommand(int& called) const |
| 135 | + { |
| 136 | + // NOLINTNEXTLINE(performance-unnecessary-value-param) |
| 137 | + return [&](std::string exe, std::vector<std::string> args, std::string redirect, std::string& /*output*/) -> int { |
| 138 | + ++called; |
| 139 | + if (exe == exename("clang-tidy")) |
| 140 | + { |
| 141 | + ASSERT_EQUALS(4, args.size()); |
| 142 | + ASSERT_EQUALS("-quiet", args[0]); |
| 143 | + ASSERT_EQUALS("-checks=*,-clang-analyzer-*,-llvm*", args[1]); |
| 144 | + ASSERT_EQUALS("test.cpp", args[2]); |
| 145 | + ASSERT_EQUALS("--", args[3]); |
| 146 | + ASSERT_EQUALS("2>&1", redirect); |
| 147 | + return EXIT_SUCCESS; |
| 148 | + } |
| 149 | + if (exe == exename("python3")) |
| 150 | + { |
| 151 | + ASSERT_EQUALS(1, args.size()); |
| 152 | + ASSERT_EQUALS("--version", args[0]); |
| 153 | + ASSERT_EQUALS("2>&1", redirect); |
| 154 | + return EXIT_SUCCESS; |
| 155 | + } |
| 156 | + if (exe == exename("python")) |
| 157 | + { |
| 158 | + ASSERT_EQUALS(1, args.size()); |
| 159 | + ASSERT_EQUALS("--version", args[0]); |
| 160 | + ASSERT_EQUALS("2>&1", redirect); |
| 161 | + return EXIT_SUCCESS; |
| 162 | + } |
| 163 | + ASSERT_MSG(false, "unhandled exe: " + exe); |
| 164 | + return EXIT_FAILURE; |
| 165 | + }; |
| 166 | + } |
| 167 | + |
| 168 | + void checkWithFileInternal(bool tools, bool nocmd = false) const |
| 169 | + { |
| 170 | + REDIRECT; |
119 | 171 | ScopedFile file("test.cpp",
|
120 | 172 | "int main()\n"
|
121 | 173 | "{\n"
|
122 | 174 | " int i = *((int*)0);\n"
|
123 | 175 | " return 0;\n"
|
124 | 176 | "}");
|
125 | 177 |
|
126 |
| - const auto s = dinit(Settings, $.templateFormat = templateFormat); |
| 178 | + int called = 0; |
| 179 | + std::unordered_set<std::string> addons; |
| 180 | + std::vector<AddonInfo> addonInfo; |
| 181 | + if (tools) |
| 182 | + { |
| 183 | + addons.emplace("testcppcheck"); |
| 184 | + addonInfo.emplace_back(AddonInfo()); |
| 185 | + } |
| 186 | + const auto s = dinit(Settings, |
| 187 | + $.templateFormat = templateFormat, |
| 188 | + $.clangTidy = tools, |
| 189 | + $.addons = std::move (addons), |
| 190 | + $.addonInfos = std::move (addonInfo)); |
127 | 191 | Suppressions supprs;
|
128 | 192 | ErrorLogger2 errorLogger;
|
129 |
| - CppCheck cppcheck(s, supprs, errorLogger, false, {}); |
| 193 | + CppCheck::ExecuteCmdFn f; |
| 194 | + if (tools && !nocmd) { |
| 195 | + f = getExecuteCommand(called); |
| 196 | + } |
| 197 | + CppCheck cppcheck(s, supprs, errorLogger, false, f); |
130 | 198 | ASSERT_EQUALS(1, cppcheck.check(FileWithDetails(file.path(), Path::identify(file.path(), false), 0)));
|
131 | 199 | // TODO: how to properly disable these warnings?
|
132 | 200 | errorLogger.ids.erase(std::remove_if(errorLogger.ids.begin(), errorLogger.ids.end(), [](const std::string& id) {
|
133 | 201 | return id == "logChecker";
|
134 | 202 | }), errorLogger.ids.end());
|
135 |
| - ASSERT_EQUALS(1, errorLogger.ids.size()); |
136 |
| - ASSERT_EQUALS("nullPointer", *errorLogger.ids.cbegin()); |
| 203 | + errorLogger.errmsgs.erase(std::remove_if(errorLogger.errmsgs.begin(), errorLogger.errmsgs.end(), [](const ErrorMessage& msg) { |
| 204 | + return msg.id == "logChecker"; |
| 205 | + }), errorLogger.errmsgs.end()); |
| 206 | + if (tools) |
| 207 | + { |
| 208 | + ASSERT_EQUALS(2, errorLogger.ids.size()); |
| 209 | + auto it = errorLogger.errmsgs.cbegin(); |
| 210 | + ASSERT_EQUALS("nullPointer", it->id); |
| 211 | + ++it; |
| 212 | + |
| 213 | + if (nocmd) |
| 214 | + { |
| 215 | + ASSERT_EQUALS("internalError", it->id); |
| 216 | + ASSERT_EQUALS("Bailing out from analysis: Checking file failed: Failed to execute addon - no command callback provided", it->shortMessage()); // TODO: add addon name |
| 217 | + |
| 218 | + // TODO: clang-tidy is currently not invoked for file inputs - see #12053 |
| 219 | + // TODO: needs to become a proper error |
| 220 | + TODO_ASSERT_EQUALS("Failed to execute '" + exename("clang-tidy") + "' (no command callback provided)\n", "", GET_REDIRECT_ERROUT); |
| 221 | + |
| 222 | + ASSERT_EQUALS(0, called); // not called because we check if the callback exists |
| 223 | + } |
| 224 | + else |
| 225 | + { |
| 226 | + ASSERT_EQUALS("internalError", it->id); |
| 227 | + ASSERT_EQUALS("Bailing out from analysis: Checking file failed: Failed to auto detect python", it->shortMessage()); // TODO: clarify what python is used for |
| 228 | + |
| 229 | + // TODO: we cannot check this because the python detection is cached globally so this result will different dependent on how the test is called |
| 230 | + //ASSERT_EQUALS(2, called); |
| 231 | + } |
| 232 | + } |
| 233 | + else |
| 234 | + { |
| 235 | + ASSERT_EQUALS(0, called); |
| 236 | + ASSERT_EQUALS(1, errorLogger.ids.size()); |
| 237 | + ASSERT_EQUALS("nullPointer", *errorLogger.ids.cbegin()); |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | + void checkWithFile() { |
| 242 | + checkWithFileInternal(false); |
137 | 243 | }
|
138 | 244 |
|
139 |
| - void checkWithFS() const |
| 245 | + void checkWithFileWithTools() { |
| 246 | + checkWithFileInternal(true); |
| 247 | + } |
| 248 | + |
| 249 | + void checkWithFileWithToolsNoCommand() { |
| 250 | + checkWithFileInternal(true, true); |
| 251 | + } |
| 252 | + |
| 253 | + void checkWithFSInternal(bool tools, bool nocmd = false) const |
140 | 254 | {
|
| 255 | + REDIRECT; |
141 | 256 | ScopedFile file("test.cpp",
|
142 | 257 | "int main()\n"
|
143 | 258 | "{\n"
|
144 | 259 | " int i = *((int*)0);\n"
|
145 | 260 | " return 0;\n"
|
146 | 261 | "}");
|
147 | 262 |
|
148 |
| - const auto s = dinit(Settings, $.templateFormat = templateFormat); |
| 263 | + int called = 0; |
| 264 | + std::unordered_set<std::string> addons; |
| 265 | + std::vector<AddonInfo> addonInfo; |
| 266 | + if (tools) |
| 267 | + { |
| 268 | + addons.emplace("testcppcheck"); |
| 269 | + addonInfo.emplace_back(AddonInfo()); |
| 270 | + } |
| 271 | + const auto s = dinit(Settings, |
| 272 | + $.templateFormat = templateFormat, |
| 273 | + $.clangTidy = tools, |
| 274 | + $.addons = std::move (addons), |
| 275 | + $.addonInfos = std::move (addonInfo)); |
149 | 276 | Suppressions supprs;
|
150 | 277 | ErrorLogger2 errorLogger;
|
151 |
| - CppCheck cppcheck(s, supprs, errorLogger, false, {}); |
| 278 | + CppCheck::ExecuteCmdFn f; |
| 279 | + if (tools && !nocmd) { |
| 280 | + f = getExecuteCommand(called); |
| 281 | + } |
| 282 | + CppCheck cppcheck(s, supprs, errorLogger, false, f); |
152 | 283 | FileSettings fs{file.path(), Path::identify(file.path(), false), 0};
|
153 | 284 | ASSERT_EQUALS(1, cppcheck.check(fs));
|
154 | 285 | // TODO: how to properly disable these warnings?
|
155 | 286 | errorLogger.ids.erase(std::remove_if(errorLogger.ids.begin(), errorLogger.ids.end(), [](const std::string& id) {
|
156 | 287 | return id == "logChecker";
|
157 | 288 | }), errorLogger.ids.end());
|
158 |
| - ASSERT_EQUALS(1, errorLogger.ids.size()); |
159 |
| - ASSERT_EQUALS("nullPointer", *errorLogger.ids.cbegin()); |
| 289 | + errorLogger.errmsgs.erase(std::remove_if(errorLogger.errmsgs.begin(), errorLogger.errmsgs.end(), [](const ErrorMessage& msg) { |
| 290 | + return msg.id == "logChecker"; |
| 291 | + }), errorLogger.errmsgs.end()); |
| 292 | + if (tools) |
| 293 | + { |
| 294 | + ASSERT_EQUALS(2, errorLogger.ids.size()); |
| 295 | + auto it = errorLogger.errmsgs.cbegin(); |
| 296 | + ASSERT_EQUALS("nullPointer", it->id); |
| 297 | + ++it; |
| 298 | + |
| 299 | + if (nocmd) |
| 300 | + { |
| 301 | + ASSERT_EQUALS("internalError", it->id); |
| 302 | + ASSERT_EQUALS("Bailing out from analysis: Checking file failed: Failed to execute addon - no command callback provided", it->shortMessage()); // TODO: add addon name |
| 303 | + |
| 304 | + // TODO: needs to become a proper error |
| 305 | + ASSERT_EQUALS("Failed to execute '" + exename("clang-tidy") + "' (no command callback provided)\n", GET_REDIRECT_ERROUT); |
| 306 | + |
| 307 | + ASSERT_EQUALS(0, called); // not called because we check if the callback exists |
| 308 | + } |
| 309 | + else |
| 310 | + { |
| 311 | + ASSERT_EQUALS("internalError", it->id); |
| 312 | + ASSERT_EQUALS("Bailing out from analysis: Checking file failed: Failed to auto detect python", it->shortMessage()); // TODO: clarify what python is used for |
| 313 | + |
| 314 | + // TODO: we cannot check this because the python detection is cached globally so this result will different dependent on how the test is called |
| 315 | + //ASSERT_EQUALS(3, called); |
| 316 | + } |
| 317 | + } |
| 318 | + else |
| 319 | + { |
| 320 | + ASSERT_EQUALS(0, called); |
| 321 | + ASSERT_EQUALS(1, errorLogger.ids.size()); |
| 322 | + ASSERT_EQUALS("nullPointer", *errorLogger.ids.cbegin()); |
| 323 | + } |
| 324 | + } |
| 325 | + |
| 326 | + void checkWithFS() { |
| 327 | + checkWithFSInternal(false); |
| 328 | + } |
| 329 | + |
| 330 | + void checkWithFSWithTools() { |
| 331 | + checkWithFSInternal(true); |
| 332 | + } |
| 333 | + |
| 334 | + void checkWithFSWithToolsNoCommand() { |
| 335 | + checkWithFSInternal(true, true); |
160 | 336 | }
|
161 | 337 |
|
162 | 338 | void suppress_error_library() const
|
|
0 commit comments