14b03ad65SVaibhav Yenamandra //===- unittests/Basic/SarifTest.cpp - Test writing SARIF documents -------===// 24b03ad65SVaibhav Yenamandra // 34b03ad65SVaibhav Yenamandra // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 44b03ad65SVaibhav Yenamandra // See https://llvm.org/LICENSE.txt for license information. 54b03ad65SVaibhav Yenamandra // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 64b03ad65SVaibhav Yenamandra // 74b03ad65SVaibhav Yenamandra //===----------------------------------------------------------------------===// 84b03ad65SVaibhav Yenamandra 94b03ad65SVaibhav Yenamandra #include "clang/Basic/Sarif.h" 104b03ad65SVaibhav Yenamandra #include "clang/Basic/DiagnosticOptions.h" 114b03ad65SVaibhav Yenamandra #include "clang/Basic/FileManager.h" 124b03ad65SVaibhav Yenamandra #include "clang/Basic/FileSystemOptions.h" 134b03ad65SVaibhav Yenamandra #include "clang/Basic/LangOptions.h" 144b03ad65SVaibhav Yenamandra #include "clang/Basic/SourceLocation.h" 154b03ad65SVaibhav Yenamandra #include "clang/Basic/SourceManager.h" 164b03ad65SVaibhav Yenamandra #include "llvm/ADT/StringRef.h" 174b03ad65SVaibhav Yenamandra #include "llvm/Support/FormatVariadic.h" 184b03ad65SVaibhav Yenamandra #include "llvm/Support/JSON.h" 194b03ad65SVaibhav Yenamandra #include "llvm/Support/MemoryBuffer.h" 204b03ad65SVaibhav Yenamandra #include "llvm/Support/VirtualFileSystem.h" 214b03ad65SVaibhav Yenamandra #include "llvm/Support/raw_ostream.h" 22313f8a20SBenjamin Kramer #include "gmock/gmock.h" 234b03ad65SVaibhav Yenamandra #include "gtest/gtest.h" 244b03ad65SVaibhav Yenamandra 254b03ad65SVaibhav Yenamandra #include <algorithm> 264b03ad65SVaibhav Yenamandra 274b03ad65SVaibhav Yenamandra using namespace clang; 284b03ad65SVaibhav Yenamandra 294b03ad65SVaibhav Yenamandra namespace { 304b03ad65SVaibhav Yenamandra 314b03ad65SVaibhav Yenamandra using LineCol = std::pair<unsigned int, unsigned int>; 324b03ad65SVaibhav Yenamandra 334b03ad65SVaibhav Yenamandra static std::string serializeSarifDocument(llvm::json::Object &&Doc) { 344b03ad65SVaibhav Yenamandra std::string Output; 351bd2b2dcSVaibhav Yenamandra llvm::json::Value Value(std::move(Doc)); 364b03ad65SVaibhav Yenamandra llvm::raw_string_ostream OS{Output}; 371bd2b2dcSVaibhav Yenamandra OS << llvm::formatv("{0}", Value); 384b03ad65SVaibhav Yenamandra return Output; 394b03ad65SVaibhav Yenamandra } 404b03ad65SVaibhav Yenamandra 414b03ad65SVaibhav Yenamandra class SarifDocumentWriterTest : public ::testing::Test { 424b03ad65SVaibhav Yenamandra protected: 434b03ad65SVaibhav Yenamandra SarifDocumentWriterTest() 444b03ad65SVaibhav Yenamandra : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), 454b03ad65SVaibhav Yenamandra FileMgr(FileSystemOptions(), InMemoryFileSystem), 464b03ad65SVaibhav Yenamandra DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()), 474b03ad65SVaibhav Yenamandra Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()), 484b03ad65SVaibhav Yenamandra SourceMgr(Diags, FileMgr) {} 494b03ad65SVaibhav Yenamandra 504b03ad65SVaibhav Yenamandra IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; 514b03ad65SVaibhav Yenamandra FileManager FileMgr; 524b03ad65SVaibhav Yenamandra IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 534b03ad65SVaibhav Yenamandra IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 544b03ad65SVaibhav Yenamandra DiagnosticsEngine Diags; 554b03ad65SVaibhav Yenamandra SourceManager SourceMgr; 564b03ad65SVaibhav Yenamandra LangOptions LangOpts; 574b03ad65SVaibhav Yenamandra 584b03ad65SVaibhav Yenamandra FileID registerSource(llvm::StringRef Name, const char *SourceText, 594b03ad65SVaibhav Yenamandra bool IsMainFile = false) { 604b03ad65SVaibhav Yenamandra std::unique_ptr<llvm::MemoryBuffer> SourceBuf = 614b03ad65SVaibhav Yenamandra llvm::MemoryBuffer::getMemBuffer(SourceText); 62*2da8f30cSJan Svoboda FileEntryRef SourceFile = 63*2da8f30cSJan Svoboda FileMgr.getVirtualFileRef(Name, SourceBuf->getBufferSize(), 0); 644b03ad65SVaibhav Yenamandra SourceMgr.overrideFileContents(SourceFile, std::move(SourceBuf)); 654b03ad65SVaibhav Yenamandra FileID FID = SourceMgr.getOrCreateFileID(SourceFile, SrcMgr::C_User); 664b03ad65SVaibhav Yenamandra if (IsMainFile) 674b03ad65SVaibhav Yenamandra SourceMgr.setMainFileID(FID); 684b03ad65SVaibhav Yenamandra return FID; 694b03ad65SVaibhav Yenamandra } 704b03ad65SVaibhav Yenamandra 714b03ad65SVaibhav Yenamandra CharSourceRange getFakeCharSourceRange(FileID FID, LineCol Begin, 724b03ad65SVaibhav Yenamandra LineCol End) { 734b03ad65SVaibhav Yenamandra auto BeginLoc = SourceMgr.translateLineCol(FID, Begin.first, Begin.second); 744b03ad65SVaibhav Yenamandra auto EndLoc = SourceMgr.translateLineCol(FID, End.first, End.second); 754b03ad65SVaibhav Yenamandra return CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false}; 764b03ad65SVaibhav Yenamandra } 774b03ad65SVaibhav Yenamandra }; 784b03ad65SVaibhav Yenamandra 794b03ad65SVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, canCreateEmptyDocument) { 804b03ad65SVaibhav Yenamandra // GIVEN: 814b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 824b03ad65SVaibhav Yenamandra 834b03ad65SVaibhav Yenamandra // WHEN: 844b03ad65SVaibhav Yenamandra const llvm::json::Object &EmptyDoc = Writer.createDocument(); 854b03ad65SVaibhav Yenamandra std::vector<StringRef> Keys(EmptyDoc.size()); 864b03ad65SVaibhav Yenamandra std::transform(EmptyDoc.begin(), EmptyDoc.end(), Keys.begin(), 871bd2b2dcSVaibhav Yenamandra [](auto Item) { return Item.getFirst(); }); 884b03ad65SVaibhav Yenamandra 894b03ad65SVaibhav Yenamandra // THEN: 904b03ad65SVaibhav Yenamandra ASSERT_THAT(Keys, testing::UnorderedElementsAre("$schema", "version")); 914b03ad65SVaibhav Yenamandra } 924b03ad65SVaibhav Yenamandra 934b03ad65SVaibhav Yenamandra // Test that a newly inserted run will associate correct tool names 944b03ad65SVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, canCreateDocumentWithOneRun) { 954b03ad65SVaibhav Yenamandra // GIVEN: 964b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 974b03ad65SVaibhav Yenamandra const char *ShortName = "sariftest"; 984b03ad65SVaibhav Yenamandra const char *LongName = "sarif writer test"; 994b03ad65SVaibhav Yenamandra 1004b03ad65SVaibhav Yenamandra // WHEN: 1014b03ad65SVaibhav Yenamandra Writer.createRun(ShortName, LongName); 1024b03ad65SVaibhav Yenamandra Writer.endRun(); 1034b03ad65SVaibhav Yenamandra const llvm::json::Object &Doc = Writer.createDocument(); 1044b03ad65SVaibhav Yenamandra const llvm::json::Array *Runs = Doc.getArray("runs"); 1054b03ad65SVaibhav Yenamandra 1064b03ad65SVaibhav Yenamandra // THEN: 1074b03ad65SVaibhav Yenamandra // A run was created 1084b03ad65SVaibhav Yenamandra ASSERT_THAT(Runs, testing::NotNull()); 1094b03ad65SVaibhav Yenamandra 1104b03ad65SVaibhav Yenamandra // It is the only run 1114b03ad65SVaibhav Yenamandra ASSERT_EQ(Runs->size(), 1UL); 1124b03ad65SVaibhav Yenamandra 1134b03ad65SVaibhav Yenamandra // The tool associated with the run was the tool 1141bd2b2dcSVaibhav Yenamandra const llvm::json::Object *Driver = 1154b03ad65SVaibhav Yenamandra Runs->begin()->getAsObject()->getObject("tool")->getObject("driver"); 1161bd2b2dcSVaibhav Yenamandra ASSERT_THAT(Driver, testing::NotNull()); 1174b03ad65SVaibhav Yenamandra 1181bd2b2dcSVaibhav Yenamandra ASSERT_TRUE(Driver->getString("name").has_value()); 1191bd2b2dcSVaibhav Yenamandra ASSERT_TRUE(Driver->getString("fullName").has_value()); 1201bd2b2dcSVaibhav Yenamandra ASSERT_TRUE(Driver->getString("language").has_value()); 1214b03ad65SVaibhav Yenamandra 122852db8e9SFangrui Song EXPECT_EQ(*Driver->getString("name"), ShortName); 123852db8e9SFangrui Song EXPECT_EQ(*Driver->getString("fullName"), LongName); 124852db8e9SFangrui Song EXPECT_EQ(*Driver->getString("language"), "en-US"); 1254b03ad65SVaibhav Yenamandra } 1264b03ad65SVaibhav Yenamandra 1274b03ad65SVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, addingResultsWillCrashIfThereIsNoRun) { 1284b03ad65SVaibhav Yenamandra #if defined(NDEBUG) || !GTEST_HAS_DEATH_TEST 1294b03ad65SVaibhav Yenamandra GTEST_SKIP() << "This death test is only available for debug builds."; 1304b03ad65SVaibhav Yenamandra #endif 1314b03ad65SVaibhav Yenamandra // GIVEN: 1324b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 1334b03ad65SVaibhav Yenamandra 1344b03ad65SVaibhav Yenamandra // WHEN: 1354b03ad65SVaibhav Yenamandra // A SarifDocumentWriter::createRun(...) was not called prior to 1364b03ad65SVaibhav Yenamandra // SarifDocumentWriter::appendResult(...) 1374b03ad65SVaibhav Yenamandra // But a rule exists 1384b03ad65SVaibhav Yenamandra auto RuleIdx = Writer.createRule(SarifRule::create()); 1394b03ad65SVaibhav Yenamandra const SarifResult &EmptyResult = SarifResult::create(RuleIdx); 1404b03ad65SVaibhav Yenamandra 1414b03ad65SVaibhav Yenamandra // THEN: 1424b03ad65SVaibhav Yenamandra auto Matcher = ::testing::AnyOf( 1434b03ad65SVaibhav Yenamandra ::testing::HasSubstr("create a run first"), 1444b03ad65SVaibhav Yenamandra ::testing::HasSubstr("no runs associated with the document")); 1454b03ad65SVaibhav Yenamandra ASSERT_DEATH(Writer.appendResult(EmptyResult), Matcher); 1464b03ad65SVaibhav Yenamandra } 1474b03ad65SVaibhav Yenamandra 1481bd2b2dcSVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, settingInvalidRankWillCrash) { 1491bd2b2dcSVaibhav Yenamandra #if defined(NDEBUG) || !GTEST_HAS_DEATH_TEST 1501bd2b2dcSVaibhav Yenamandra GTEST_SKIP() << "This death test is only available for debug builds."; 1511bd2b2dcSVaibhav Yenamandra #endif 1521bd2b2dcSVaibhav Yenamandra // GIVEN: 1531bd2b2dcSVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 1541bd2b2dcSVaibhav Yenamandra 1551bd2b2dcSVaibhav Yenamandra // WHEN: 1561bd2b2dcSVaibhav Yenamandra // A SarifReportingConfiguration is created with an invalid "rank" 1571bd2b2dcSVaibhav Yenamandra // * Ranks below 0.0 are invalid 1581bd2b2dcSVaibhav Yenamandra // * Ranks above 100.0 are invalid 1591bd2b2dcSVaibhav Yenamandra 1601bd2b2dcSVaibhav Yenamandra // THEN: The builder will crash in either case 1611bd2b2dcSVaibhav Yenamandra EXPECT_DEATH(SarifReportingConfiguration::create().setRank(-1.0), 1621bd2b2dcSVaibhav Yenamandra ::testing::HasSubstr("Rule rank cannot be smaller than 0.0")); 1631bd2b2dcSVaibhav Yenamandra EXPECT_DEATH(SarifReportingConfiguration::create().setRank(101.0), 1641bd2b2dcSVaibhav Yenamandra ::testing::HasSubstr("Rule rank cannot be larger than 100.0")); 1651bd2b2dcSVaibhav Yenamandra } 1661bd2b2dcSVaibhav Yenamandra 1671bd2b2dcSVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, creatingResultWithDisabledRuleWillCrash) { 1681bd2b2dcSVaibhav Yenamandra #if defined(NDEBUG) || !GTEST_HAS_DEATH_TEST 1691bd2b2dcSVaibhav Yenamandra GTEST_SKIP() << "This death test is only available for debug builds."; 1701bd2b2dcSVaibhav Yenamandra #endif 1711bd2b2dcSVaibhav Yenamandra 1721bd2b2dcSVaibhav Yenamandra // GIVEN: 1731bd2b2dcSVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 1741bd2b2dcSVaibhav Yenamandra 1751bd2b2dcSVaibhav Yenamandra // WHEN: 1761bd2b2dcSVaibhav Yenamandra // A disabled Rule is created, and a result is create referencing this rule 1771bd2b2dcSVaibhav Yenamandra const auto &Config = SarifReportingConfiguration::create().disable(); 1781bd2b2dcSVaibhav Yenamandra auto RuleIdx = 1791bd2b2dcSVaibhav Yenamandra Writer.createRule(SarifRule::create().setDefaultConfiguration(Config)); 1801bd2b2dcSVaibhav Yenamandra const SarifResult &Result = SarifResult::create(RuleIdx); 1811bd2b2dcSVaibhav Yenamandra 1821bd2b2dcSVaibhav Yenamandra // THEN: 1831bd2b2dcSVaibhav Yenamandra // SarifResult::create(...) will produce a crash 1841bd2b2dcSVaibhav Yenamandra ASSERT_DEATH( 1851bd2b2dcSVaibhav Yenamandra Writer.appendResult(Result), 1861bd2b2dcSVaibhav Yenamandra ::testing::HasSubstr("Cannot add a result referencing a disabled Rule")); 1871bd2b2dcSVaibhav Yenamandra } 1881bd2b2dcSVaibhav Yenamandra 1894b03ad65SVaibhav Yenamandra // Test adding rule and result shows up in the final document 1904b03ad65SVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, addingResultWithValidRuleAndRunIsOk) { 1914b03ad65SVaibhav Yenamandra // GIVEN: 1924b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 1934b03ad65SVaibhav Yenamandra const SarifRule &Rule = 1944b03ad65SVaibhav Yenamandra SarifRule::create() 1954b03ad65SVaibhav Yenamandra .setRuleId("clang.unittest") 1964b03ad65SVaibhav Yenamandra .setDescription("Example rule created during unit tests") 1974b03ad65SVaibhav Yenamandra .setName("clang unit test"); 1984b03ad65SVaibhav Yenamandra 1994b03ad65SVaibhav Yenamandra // WHEN: 2004b03ad65SVaibhav Yenamandra Writer.createRun("sarif test", "sarif test runner"); 2014b03ad65SVaibhav Yenamandra unsigned RuleIdx = Writer.createRule(Rule); 2021bd2b2dcSVaibhav Yenamandra const SarifResult &Result = SarifResult::create(RuleIdx); 2034b03ad65SVaibhav Yenamandra 2041bd2b2dcSVaibhav Yenamandra Writer.appendResult(Result); 2054b03ad65SVaibhav Yenamandra const llvm::json::Object &Doc = Writer.createDocument(); 2064b03ad65SVaibhav Yenamandra 2074b03ad65SVaibhav Yenamandra // THEN: 2084b03ad65SVaibhav Yenamandra // A document with a valid schema and version exists 2094b03ad65SVaibhav Yenamandra ASSERT_THAT(Doc.get("$schema"), ::testing::NotNull()); 2104b03ad65SVaibhav Yenamandra ASSERT_THAT(Doc.get("version"), ::testing::NotNull()); 2114b03ad65SVaibhav Yenamandra const llvm::json::Array *Runs = Doc.getArray("runs"); 2124b03ad65SVaibhav Yenamandra 2134b03ad65SVaibhav Yenamandra // A run exists on this document 2144b03ad65SVaibhav Yenamandra ASSERT_THAT(Runs, ::testing::NotNull()); 2154b03ad65SVaibhav Yenamandra ASSERT_EQ(Runs->size(), 1UL); 2164b03ad65SVaibhav Yenamandra const llvm::json::Object *TheRun = Runs->back().getAsObject(); 2174b03ad65SVaibhav Yenamandra 2184b03ad65SVaibhav Yenamandra // The run has slots for tools, results, rules and artifacts 2194b03ad65SVaibhav Yenamandra ASSERT_THAT(TheRun->get("tool"), ::testing::NotNull()); 2204b03ad65SVaibhav Yenamandra ASSERT_THAT(TheRun->get("results"), ::testing::NotNull()); 2214b03ad65SVaibhav Yenamandra ASSERT_THAT(TheRun->get("artifacts"), ::testing::NotNull()); 2224b03ad65SVaibhav Yenamandra const llvm::json::Object *Driver = 2234b03ad65SVaibhav Yenamandra TheRun->getObject("tool")->getObject("driver"); 2244b03ad65SVaibhav Yenamandra const llvm::json::Array *Results = TheRun->getArray("results"); 2254b03ad65SVaibhav Yenamandra const llvm::json::Array *Artifacts = TheRun->getArray("artifacts"); 2264b03ad65SVaibhav Yenamandra 2274b03ad65SVaibhav Yenamandra // The tool is as expected 22841ae78eaSKazu Hirata ASSERT_TRUE(Driver->getString("name").has_value()); 22941ae78eaSKazu Hirata ASSERT_TRUE(Driver->getString("fullName").has_value()); 2304b03ad65SVaibhav Yenamandra 231852db8e9SFangrui Song EXPECT_EQ(*Driver->getString("name"), "sarif test"); 232852db8e9SFangrui Song EXPECT_EQ(*Driver->getString("fullName"), "sarif test runner"); 2334b03ad65SVaibhav Yenamandra 2344b03ad65SVaibhav Yenamandra // The results are as expected 2354b03ad65SVaibhav Yenamandra EXPECT_EQ(Results->size(), 1UL); 2364b03ad65SVaibhav Yenamandra 2374b03ad65SVaibhav Yenamandra // The artifacts are as expected 2384b03ad65SVaibhav Yenamandra EXPECT_TRUE(Artifacts->empty()); 2394b03ad65SVaibhav Yenamandra } 2404b03ad65SVaibhav Yenamandra 2411bd2b2dcSVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, checkSerializingResultsWithDefaultRuleConfig) { 2424b03ad65SVaibhav Yenamandra // GIVEN: 2434b03ad65SVaibhav Yenamandra const std::string ExpectedOutput = 2441bd2b2dcSVaibhav Yenamandra R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[],"columnKind":"unicodeCodePoints","results":[{"level":"warning","message":{"text":""},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; 2454b03ad65SVaibhav Yenamandra 2464b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 2474b03ad65SVaibhav Yenamandra const SarifRule &Rule = 2484b03ad65SVaibhav Yenamandra SarifRule::create() 2494b03ad65SVaibhav Yenamandra .setRuleId("clang.unittest") 2504b03ad65SVaibhav Yenamandra .setDescription("Example rule created during unit tests") 2514b03ad65SVaibhav Yenamandra .setName("clang unit test"); 2524b03ad65SVaibhav Yenamandra 2534b03ad65SVaibhav Yenamandra // WHEN: A run contains a result 2544b03ad65SVaibhav Yenamandra Writer.createRun("sarif test", "sarif test runner", "1.0.0"); 2551bd2b2dcSVaibhav Yenamandra unsigned RuleIdx = Writer.createRule(Rule); 2561bd2b2dcSVaibhav Yenamandra const SarifResult &Result = SarifResult::create(RuleIdx); 2571bd2b2dcSVaibhav Yenamandra Writer.appendResult(Result); 2581bd2b2dcSVaibhav Yenamandra std::string Output = serializeSarifDocument(Writer.createDocument()); 2591bd2b2dcSVaibhav Yenamandra 2601bd2b2dcSVaibhav Yenamandra // THEN: 2611bd2b2dcSVaibhav Yenamandra ASSERT_THAT(Output, ::testing::StrEq(ExpectedOutput)); 2621bd2b2dcSVaibhav Yenamandra } 2631bd2b2dcSVaibhav Yenamandra 2641bd2b2dcSVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, checkSerializingResultsWithCustomRuleConfig) { 2651bd2b2dcSVaibhav Yenamandra // GIVEN: 2661bd2b2dcSVaibhav Yenamandra const std::string ExpectedOutput = 2671bd2b2dcSVaibhav Yenamandra R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[],"columnKind":"unicodeCodePoints","results":[{"level":"error","message":{"text":""},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"defaultConfiguration":{"enabled":true,"level":"error","rank":35.5},"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; 2681bd2b2dcSVaibhav Yenamandra 2691bd2b2dcSVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 2701bd2b2dcSVaibhav Yenamandra const SarifRule &Rule = 2711bd2b2dcSVaibhav Yenamandra SarifRule::create() 2721bd2b2dcSVaibhav Yenamandra .setRuleId("clang.unittest") 2731bd2b2dcSVaibhav Yenamandra .setDescription("Example rule created during unit tests") 2741bd2b2dcSVaibhav Yenamandra .setName("clang unit test") 2751bd2b2dcSVaibhav Yenamandra .setDefaultConfiguration(SarifReportingConfiguration::create() 2761bd2b2dcSVaibhav Yenamandra .setLevel(SarifResultLevel::Error) 2771bd2b2dcSVaibhav Yenamandra .setRank(35.5)); 2781bd2b2dcSVaibhav Yenamandra 2791bd2b2dcSVaibhav Yenamandra // WHEN: A run contains a result 2801bd2b2dcSVaibhav Yenamandra Writer.createRun("sarif test", "sarif test runner", "1.0.0"); 2811bd2b2dcSVaibhav Yenamandra unsigned RuleIdx = Writer.createRule(Rule); 2821bd2b2dcSVaibhav Yenamandra const SarifResult &Result = SarifResult::create(RuleIdx); 2834b03ad65SVaibhav Yenamandra Writer.appendResult(Result); 2844b03ad65SVaibhav Yenamandra std::string Output = serializeSarifDocument(Writer.createDocument()); 2854b03ad65SVaibhav Yenamandra 2864b03ad65SVaibhav Yenamandra // THEN: 2874b03ad65SVaibhav Yenamandra ASSERT_THAT(Output, ::testing::StrEq(ExpectedOutput)); 2884b03ad65SVaibhav Yenamandra } 2894b03ad65SVaibhav Yenamandra 2904b03ad65SVaibhav Yenamandra // Check that serializing artifacts from results produces valid SARIF 2914b03ad65SVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, checkSerializingArtifacts) { 2924b03ad65SVaibhav Yenamandra // GIVEN: 2934b03ad65SVaibhav Yenamandra const std::string ExpectedOutput = 2947b6fe711SVaibhav Yenamandra R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":40,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:///main.cpp"},"region":{"endColumn":14,"startColumn":14,"startLine":3}}}],"message":{"text":"expected ';' after top level declarator"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; 2954b03ad65SVaibhav Yenamandra 2964b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 2974b03ad65SVaibhav Yenamandra const SarifRule &Rule = 2984b03ad65SVaibhav Yenamandra SarifRule::create() 2994b03ad65SVaibhav Yenamandra .setRuleId("clang.unittest") 3004b03ad65SVaibhav Yenamandra .setDescription("Example rule created during unit tests") 3014b03ad65SVaibhav Yenamandra .setName("clang unit test"); 3024b03ad65SVaibhav Yenamandra 3034b03ad65SVaibhav Yenamandra // WHEN: A result is added with valid source locations for its diagnostics 3044b03ad65SVaibhav Yenamandra Writer.createRun("sarif test", "sarif test runner", "1.0.0"); 3054b03ad65SVaibhav Yenamandra unsigned RuleIdx = Writer.createRule(Rule); 3064b03ad65SVaibhav Yenamandra 3074b03ad65SVaibhav Yenamandra llvm::SmallVector<CharSourceRange, 1> DiagLocs; 3084b03ad65SVaibhav Yenamandra const char *SourceText = "int foo = 0;\n" 3094b03ad65SVaibhav Yenamandra "int bar = 1;\n" 3104b03ad65SVaibhav Yenamandra "float x = 0.0\n"; 3114b03ad65SVaibhav Yenamandra 3124b03ad65SVaibhav Yenamandra FileID MainFileID = 3134b03ad65SVaibhav Yenamandra registerSource("/main.cpp", SourceText, /* IsMainFile = */ true); 3144b03ad65SVaibhav Yenamandra CharSourceRange SourceCSR = 3154b03ad65SVaibhav Yenamandra getFakeCharSourceRange(MainFileID, {3, 14}, {3, 14}); 3164b03ad65SVaibhav Yenamandra 3174b03ad65SVaibhav Yenamandra DiagLocs.push_back(SourceCSR); 3184b03ad65SVaibhav Yenamandra 3194b03ad65SVaibhav Yenamandra const SarifResult &Result = 3201bd2b2dcSVaibhav Yenamandra SarifResult::create(RuleIdx) 3211bd2b2dcSVaibhav Yenamandra .setLocations(DiagLocs) 3221bd2b2dcSVaibhav Yenamandra .setDiagnosticMessage("expected ';' after top level declarator") 3231bd2b2dcSVaibhav Yenamandra .setDiagnosticLevel(SarifResultLevel::Error); 3244b03ad65SVaibhav Yenamandra Writer.appendResult(Result); 3254b03ad65SVaibhav Yenamandra std::string Output = serializeSarifDocument(Writer.createDocument()); 3264b03ad65SVaibhav Yenamandra 3274b03ad65SVaibhav Yenamandra // THEN: Assert that the serialized SARIF is as expected 3284b03ad65SVaibhav Yenamandra ASSERT_THAT(Output, ::testing::StrEq(ExpectedOutput)); 3294b03ad65SVaibhav Yenamandra } 3304b03ad65SVaibhav Yenamandra 3314b03ad65SVaibhav Yenamandra TEST_F(SarifDocumentWriterTest, checkSerializingCodeflows) { 3324b03ad65SVaibhav Yenamandra // GIVEN: 3334b03ad65SVaibhav Yenamandra const std::string ExpectedOutput = 334dda8ac8dSFangrui Song R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":41,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]},{"length":27,"location":{"index":1,"uri":"file:///test-header-1.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":30,"location":{"index":2,"uri":"file:///test-header-2.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":28,"location":{"index":3,"uri":"file:///test-header-3.h"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"codeFlows":[{"threadFlows":[{"locations":[{"importance":"essential","location":{"message":{"text":"Message #1"},"physicalLocation":{"artifactLocation":{"index":1,"uri":"file:///test-header-1.h"},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}},{"importance":"important","location":{"message":{"text":"Message #2"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"file:///test-header-2.h"},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}},{"importance":"unimportant","location":{"message":{"text":"Message #3"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"file:///test-header-3.h"},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}}]}]}],"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:///main.cpp"},"region":{"endColumn":8,"endLine":2,"startColumn":5,"startLine":2}}}],"message":{"text":"Redefinition of 'foo'"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; 3354b03ad65SVaibhav Yenamandra 3364b03ad65SVaibhav Yenamandra const char *SourceText = "int foo = 0;\n" 3374b03ad65SVaibhav Yenamandra "int foo = 1;\n" 3384b03ad65SVaibhav Yenamandra "float x = 0.0;\n"; 3394b03ad65SVaibhav Yenamandra FileID MainFileID = 3404b03ad65SVaibhav Yenamandra registerSource("/main.cpp", SourceText, /* IsMainFile = */ true); 3414b03ad65SVaibhav Yenamandra CharSourceRange DiagLoc{getFakeCharSourceRange(MainFileID, {2, 5}, {2, 8})}; 3424b03ad65SVaibhav Yenamandra 3434b03ad65SVaibhav Yenamandra SarifDocumentWriter Writer{SourceMgr}; 3444b03ad65SVaibhav Yenamandra const SarifRule &Rule = 3454b03ad65SVaibhav Yenamandra SarifRule::create() 3464b03ad65SVaibhav Yenamandra .setRuleId("clang.unittest") 3474b03ad65SVaibhav Yenamandra .setDescription("Example rule created during unit tests") 3484b03ad65SVaibhav Yenamandra .setName("clang unit test"); 3494b03ad65SVaibhav Yenamandra 3501bd2b2dcSVaibhav Yenamandra constexpr unsigned int NumCases = 3; 3511bd2b2dcSVaibhav Yenamandra llvm::SmallVector<ThreadFlow, NumCases> Threadflows; 3521bd2b2dcSVaibhav Yenamandra const char *HeaderTexts[NumCases]{("#pragma once\n" 3534b03ad65SVaibhav Yenamandra "#include <foo>"), 3544b03ad65SVaibhav Yenamandra ("#ifndef FOO\n" 3554b03ad65SVaibhav Yenamandra "#define FOO\n" 3564b03ad65SVaibhav Yenamandra "#endif"), 3574b03ad65SVaibhav Yenamandra ("#ifdef FOO\n" 3584b03ad65SVaibhav Yenamandra "#undef FOO\n" 3594b03ad65SVaibhav Yenamandra "#endif")}; 3601bd2b2dcSVaibhav Yenamandra const char *HeaderNames[NumCases]{"/test-header-1.h", "/test-header-2.h", 3614b03ad65SVaibhav Yenamandra "/test-header-3.h"}; 3621bd2b2dcSVaibhav Yenamandra ThreadFlowImportance Importances[NumCases]{ThreadFlowImportance::Essential, 3631bd2b2dcSVaibhav Yenamandra ThreadFlowImportance::Important, 3644b03ad65SVaibhav Yenamandra ThreadFlowImportance::Unimportant}; 3651bd2b2dcSVaibhav Yenamandra for (size_t Idx = 0; Idx != NumCases; ++Idx) { 3664b03ad65SVaibhav Yenamandra FileID FID = registerSource(HeaderNames[Idx], HeaderTexts[Idx]); 3674b03ad65SVaibhav Yenamandra CharSourceRange &&CSR = getFakeCharSourceRange(FID, {1, 1}, {2, 8}); 3684b03ad65SVaibhav Yenamandra std::string Message = llvm::formatv("Message #{0}", Idx + 1); 3694b03ad65SVaibhav Yenamandra ThreadFlow Item = ThreadFlow::create() 3704b03ad65SVaibhav Yenamandra .setRange(CSR) 3714b03ad65SVaibhav Yenamandra .setImportance(Importances[Idx]) 3724b03ad65SVaibhav Yenamandra .setMessage(Message); 3734b03ad65SVaibhav Yenamandra Threadflows.push_back(Item); 3744b03ad65SVaibhav Yenamandra } 3754b03ad65SVaibhav Yenamandra 3764b03ad65SVaibhav Yenamandra // WHEN: A result containing code flows and diagnostic locations is added 3774b03ad65SVaibhav Yenamandra Writer.createRun("sarif test", "sarif test runner", "1.0.0"); 3784b03ad65SVaibhav Yenamandra unsigned RuleIdx = Writer.createRule(Rule); 3791bd2b2dcSVaibhav Yenamandra const SarifResult &Result = 3801bd2b2dcSVaibhav Yenamandra SarifResult::create(RuleIdx) 3814b03ad65SVaibhav Yenamandra .setLocations({DiagLoc}) 3824b03ad65SVaibhav Yenamandra .setDiagnosticMessage("Redefinition of 'foo'") 3831bd2b2dcSVaibhav Yenamandra .setThreadFlows(Threadflows) 3841bd2b2dcSVaibhav Yenamandra .setDiagnosticLevel(SarifResultLevel::Warning); 3854b03ad65SVaibhav Yenamandra Writer.appendResult(Result); 3864b03ad65SVaibhav Yenamandra std::string Output = serializeSarifDocument(Writer.createDocument()); 3874b03ad65SVaibhav Yenamandra 3884b03ad65SVaibhav Yenamandra // THEN: Assert that the serialized SARIF is as expected 3894b03ad65SVaibhav Yenamandra ASSERT_THAT(Output, ::testing::StrEq(ExpectedOutput)); 3904b03ad65SVaibhav Yenamandra } 3914b03ad65SVaibhav Yenamandra 3924b03ad65SVaibhav Yenamandra } // namespace 393