xref: /llvm-project/clang/unittests/Basic/SarifTest.cpp (revision 7db641af13670aa1f1ecd3106eda3ce447afd752)
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