Skip to content

Commit c03b22e

Browse files
committed
add test case to check instance specific error messages
1 parent 94c7c76 commit c03b22e

1 file changed

Lines changed: 159 additions & 3 deletions

File tree

test/testsarif.cpp

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class TestSarif : public TestFixture
2929
{
3030
public:
3131
TestSarif() : TestFixture("TestSarif")
32-
{}
32+
{
33+
}
3334

3435
private:
3536
// Shared test code with various error types
@@ -245,6 +246,7 @@ int main() {
245246
TEST_CASE(sarifSecuritySeverity);
246247
TEST_CASE(sarifLocationInfo);
247248
TEST_CASE(sarifGenericDescriptions);
249+
TEST_CASE(sarifInstanceSpecificMessages);
248250
TEST_CASE(sarifCweTags);
249251
TEST_CASE(sarifRuleCoverage);
250252
TEST_CASE(sarifSeverityLevels);
@@ -406,17 +408,37 @@ int main() {
406408

407409
ASSERT(results.size() > 0);
408410

409-
// Check that we have different severity levels
410-
bool hasError = false;
411+
// Check that we have different severity levels and meaningful messages
412+
bool hasError = false;
413+
bool hasNonEmptyMessage = false;
414+
411415
for (const auto& result : results)
412416
{
413417
const picojson::object& res = result.get<picojson::object>();
414418
const std::string level = res.at("level").get<std::string>();
415419
if (level == "error")
416420
hasError = true;
421+
422+
// Verify each result has a meaningful message
423+
ASSERT(res.find("message") != res.end());
424+
const picojson::object& message = res.at("message").get<picojson::object>();
425+
ASSERT(message.find("text") != message.end());
426+
const std::string messageText = message.at("text").get<std::string>();
427+
428+
// Messages should be non-empty and meaningful
429+
ASSERT(messageText.length() > 0);
430+
if (messageText.length() > 5) // Reasonable minimum for a meaningful message
431+
{
432+
hasNonEmptyMessage = true;
433+
}
434+
435+
// Basic validation that messages don't contain obvious placeholders
436+
ASSERT_EQUALS(std::string::npos, messageText.find("{{"));
437+
ASSERT_EQUALS(std::string::npos, messageText.find("}}"));
417438
}
418439

419440
ASSERT_EQUALS(true, hasError);
441+
ASSERT_EQUALS(true, hasNonEmptyMessage);
420442
}
421443

422444
void sarifRuleDescriptions()
@@ -607,6 +629,140 @@ int main() {
607629
}
608630
}
609631

632+
void sarifInstanceSpecificMessages()
633+
{
634+
// Use the global testCode to validate instance-specific messages
635+
const std::string sarif = runCppcheckSarif(testCode);
636+
637+
std::string errorMsg;
638+
ASSERT_EQUALS(true, validateSarifJson(sarif, errorMsg));
639+
640+
picojson::value json;
641+
picojson::parse(json, sarif);
642+
const picojson::object& root = json.get<picojson::object>();
643+
const picojson::array& runs = root.at("runs").get<picojson::array>();
644+
const picojson::object& run = runs[0].get<picojson::object>();
645+
const picojson::array& results = run.at("results").get<picojson::array>();
646+
647+
ASSERT(results.size() > 0);
648+
649+
// Validate that results contain instance-specific information
650+
bool foundArrayBoundsWithSpecifics = false;
651+
bool foundAnyInstanceSpecificMessage = false;
652+
653+
for (const auto& result : results)
654+
{
655+
const picojson::object& res = result.get<picojson::object>();
656+
const std::string ruleId = res.at("ruleId").get<std::string>();
657+
const picojson::object& message = res.at("message").get<picojson::object>();
658+
const std::string messageText = message.at("text").get<std::string>();
659+
660+
// Skip system include warnings as they're not relevant to our test
661+
if (ruleId == "missingIncludeSystem")
662+
continue;
663+
664+
if (ruleId == "arrayIndexOutOfBounds")
665+
{
666+
// Should contain specific array name and/or index from testCode
667+
if ((messageText.find("array") != std::string::npos && messageText.find("10") != std::string::npos) ||
668+
(messageText.find("Array") != std::string::npos && messageText.find("10") != std::string::npos))
669+
{
670+
foundArrayBoundsWithSpecifics = true;
671+
foundAnyInstanceSpecificMessage = true;
672+
// Verify it's about array bounds
673+
ASSERT(messageText.find("bound") != std::string::npos ||
674+
messageText.find("index") != std::string::npos ||
675+
messageText.find("Array") != std::string::npos);
676+
}
677+
}
678+
else if (ruleId == "nullPointer")
679+
{
680+
// Should contain the specific variable name from our test (ptr)
681+
if (messageText.find("ptr") != std::string::npos)
682+
{
683+
foundAnyInstanceSpecificMessage = true;
684+
// Verify it's a meaningful message about null pointer dereference
685+
ASSERT(messageText.find("null") != std::string::npos ||
686+
messageText.find("nullptr") != std::string::npos ||
687+
messageText.find("NULL") != std::string::npos ||
688+
messageText.find("Null") != std::string::npos);
689+
}
690+
}
691+
else if (ruleId == "memleak")
692+
{
693+
// Should contain the specific variable name from testCode (mem)
694+
if (messageText.find("mem") != std::string::npos)
695+
{
696+
foundAnyInstanceSpecificMessage = true;
697+
// Verify it's about memory leak
698+
ASSERT(messageText.find("leak") != std::string::npos ||
699+
messageText.find("free") != std::string::npos ||
700+
messageText.find("memory") != std::string::npos);
701+
}
702+
}
703+
else if (ruleId == "uninitvar")
704+
{
705+
// Should contain the specific variable name from testCode (x)
706+
if (messageText.find("'x'") != std::string::npos)
707+
{
708+
foundAnyInstanceSpecificMessage = true;
709+
// Verify it's about uninitialized variable
710+
ASSERT(messageText.find("uninit") != std::string::npos ||
711+
messageText.find("initial") != std::string::npos);
712+
}
713+
}
714+
else if (ruleId == "unusedVariable")
715+
{
716+
// Should contain specific variable names from testCode (unused, redundant, etc.)
717+
if (messageText.find("unused") != std::string::npos ||
718+
messageText.find("redundant") != std::string::npos ||
719+
messageText.find("counter") != std::string::npos)
720+
{
721+
foundAnyInstanceSpecificMessage = true;
722+
// Verify it's about unused variable
723+
ASSERT(messageText.find("unused") != std::string::npos ||
724+
messageText.find("never used") != std::string::npos);
725+
}
726+
}
727+
else if (ruleId == "doubleFree")
728+
{
729+
// Should contain specific variable name from testCode (p)
730+
if (messageText.find("'p'") != std::string::npos)
731+
{
732+
foundAnyInstanceSpecificMessage = true;
733+
// Verify it's about double free
734+
ASSERT(messageText.find("free") != std::string::npos ||
735+
messageText.find("deallocat") != std::string::npos);
736+
}
737+
}
738+
else if (ruleId == "redundantAssignment")
739+
{
740+
// Should contain specific variable name from testCode (redundant)
741+
if (messageText.find("redundant") != std::string::npos)
742+
{
743+
foundAnyInstanceSpecificMessage = true;
744+
// Verify it's about redundant assignment
745+
ASSERT(messageText.find("redundant") != std::string::npos ||
746+
messageText.find("assign") != std::string::npos);
747+
}
748+
}
749+
750+
// Verify that all messages are non-empty and meaningful
751+
ASSERT(messageText.length() > 0);
752+
ASSERT(messageText != "");
753+
754+
// Messages should not contain generic placeholders
755+
ASSERT_EQUALS(std::string::npos, messageText.find("{{"));
756+
ASSERT_EQUALS(std::string::npos, messageText.find("}}"));
757+
}
758+
759+
// We must find at least the array bounds violation with specific details
760+
ASSERT_EQUALS(true, foundArrayBoundsWithSpecifics);
761+
762+
// We should find at least one instance-specific message overall
763+
ASSERT_EQUALS(true, foundAnyInstanceSpecificMessage);
764+
}
765+
610766
void sarifCweTags()
611767
{
612768
const std::string sarif = runCppcheckSarif(testCode);

0 commit comments

Comments
 (0)