|  | //===-- JSON Tests --------------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "JSON.h" | 
|  | #include "LibcBenchmark.h" | 
|  | #include "LibcMemoryBenchmark.h" | 
|  | #include "llvm/Support/JSON.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include "gmock/gmock.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using testing::AllOf; | 
|  | using testing::ExplainMatchResult; | 
|  | using testing::Field; | 
|  | using testing::Pointwise; | 
|  |  | 
|  | namespace llvm { | 
|  | namespace libc_benchmarks { | 
|  | namespace { | 
|  |  | 
|  | Study getStudy() { | 
|  | return Study{ | 
|  | "StudyName", | 
|  | Runtime{HostState{"CpuName", | 
|  | 123, | 
|  | {CacheInfo{"A", 1, 2, 3}, CacheInfo{"B", 4, 5, 6}}}, | 
|  | 456, 789, | 
|  | BenchmarkOptions{std::chrono::seconds(1), std::chrono::seconds(2), | 
|  | 10, 100, 6, 100, 0.1, 2, BenchmarkLog::Full}}, | 
|  | StudyConfiguration{std::string("Function"), 30U, false, 32U, | 
|  | std::string("Distribution"), Align(16), 3U}, | 
|  | {std::chrono::seconds(3), std::chrono::seconds(4)}}; | 
|  | } | 
|  |  | 
|  | static std::string serializeToString(const Study &S) { | 
|  | std::string Buffer; | 
|  | raw_string_ostream RSO(Buffer); | 
|  | json::OStream JOS(RSO); | 
|  | serializeToJson(S, JOS); | 
|  | return Buffer; | 
|  | } | 
|  |  | 
|  | MATCHER(EqualsCacheInfo, "") { | 
|  | const CacheInfo &A = ::testing::get<0>(arg); | 
|  | const CacheInfo &B = ::testing::get<1>(arg); | 
|  | return ExplainMatchResult(AllOf(Field(&CacheInfo::Type, B.Type), | 
|  | Field(&CacheInfo::Level, B.Level), | 
|  | Field(&CacheInfo::Size, B.Size), | 
|  | Field(&CacheInfo::NumSharing, B.NumSharing)), | 
|  | A, result_listener); | 
|  | } | 
|  |  | 
|  | auto equals(const HostState &H) -> auto { | 
|  | return AllOf( | 
|  | Field(&HostState::CpuName, H.CpuName), | 
|  | Field(&HostState::CpuFrequency, H.CpuFrequency), | 
|  | Field(&HostState::Caches, Pointwise(EqualsCacheInfo(), H.Caches))); | 
|  | } | 
|  |  | 
|  | auto equals(const StudyConfiguration &SC) -> auto { | 
|  | return AllOf( | 
|  | Field(&StudyConfiguration::Function, SC.Function), | 
|  | Field(&StudyConfiguration::NumTrials, SC.NumTrials), | 
|  | Field(&StudyConfiguration::IsSweepMode, SC.IsSweepMode), | 
|  | Field(&StudyConfiguration::SweepModeMaxSize, SC.SweepModeMaxSize), | 
|  | Field(&StudyConfiguration::SizeDistributionName, SC.SizeDistributionName), | 
|  | Field(&StudyConfiguration::AccessAlignment, SC.AccessAlignment), | 
|  | Field(&StudyConfiguration::MemcmpMismatchAt, SC.MemcmpMismatchAt)); | 
|  | } | 
|  |  | 
|  | auto equals(const BenchmarkOptions &BO) -> auto { | 
|  | return AllOf( | 
|  | Field(&BenchmarkOptions::MinDuration, BO.MinDuration), | 
|  | Field(&BenchmarkOptions::MaxDuration, BO.MaxDuration), | 
|  | Field(&BenchmarkOptions::InitialIterations, BO.InitialIterations), | 
|  | Field(&BenchmarkOptions::MaxIterations, BO.MaxIterations), | 
|  | Field(&BenchmarkOptions::MinSamples, BO.MinSamples), | 
|  | Field(&BenchmarkOptions::MaxSamples, BO.MaxSamples), | 
|  | Field(&BenchmarkOptions::Epsilon, BO.Epsilon), | 
|  | Field(&BenchmarkOptions::ScalingFactor, BO.ScalingFactor), | 
|  | Field(&BenchmarkOptions::Log, BO.Log)); | 
|  | } | 
|  |  | 
|  | auto equals(const Runtime &RI) -> auto { | 
|  | return AllOf(Field(&Runtime::Host, equals(RI.Host)), | 
|  | Field(&Runtime::BufferSize, RI.BufferSize), | 
|  | Field(&Runtime::BatchParameterCount, RI.BatchParameterCount), | 
|  | Field(&Runtime::BenchmarkOptions, equals(RI.BenchmarkOptions))); | 
|  | } | 
|  |  | 
|  | auto equals(const Study &S) -> auto { | 
|  | return AllOf(Field(&Study::StudyName, S.StudyName), | 
|  | Field(&Study::Runtime, equals(S.Runtime)), | 
|  | Field(&Study::Configuration, equals(S.Configuration)), | 
|  | Field(&Study::Measurements, S.Measurements)); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, RoundTrip) { | 
|  | const Study S = getStudy(); | 
|  | const auto Serialized = serializeToString(S); | 
|  | auto StudyOrError = parseJsonStudy(Serialized); | 
|  | if (auto Err = StudyOrError.takeError()) | 
|  | EXPECT_FALSE(Err) << "Unexpected error : " << Err << "\n" << Serialized; | 
|  | const Study &Parsed = *StudyOrError; | 
|  | EXPECT_THAT(Parsed, equals(S)) << Serialized << "\n" | 
|  | << serializeToString(Parsed); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, SupplementaryField) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "UnknownField": 10 | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Unknown field: UnknownField"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidType) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "Runtime": 1 | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Expected JSON Object"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidDuration) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "Runtime": { | 
|  | "BenchmarkOptions": { | 
|  | "MinDuration": "Duration should be a Number" | 
|  | } | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Can't parse Duration"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidAlignType) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "Configuration": { | 
|  | "AccessAlignment": "Align should be an Integer" | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Can't parse Align, not an Integer"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidAlign) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "Configuration": { | 
|  | "AccessAlignment": 3 | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), | 
|  | "Can't parse Align, not a power of two"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidBenchmarkLogType) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "Runtime": { | 
|  | "BenchmarkOptions":{ | 
|  | "Log": 3 | 
|  | } | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), | 
|  | "Can't parse BenchmarkLog, not a String"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidBenchmarkLog) { | 
|  | auto Failure = parseJsonStudy(R"({ | 
|  | "Runtime": { | 
|  | "BenchmarkOptions":{ | 
|  | "Log": "Unknown" | 
|  | } | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), | 
|  | "Can't parse BenchmarkLog, invalid value 'Unknown'"); | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  | } // namespace libc_benchmarks | 
|  | } // namespace llvm |