|  | //===-- 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{ | 
|  | HostState{ | 
|  | "CpuName", 123, {CacheInfo{"A", 1, 2, 3}, CacheInfo{"B", 4, 5, 6}}}, | 
|  | BenchmarkOptions{std::chrono::seconds(1), std::chrono::seconds(2), 10, | 
|  | 100, 6, 100, 0.1, 2, BenchmarkLog::Full}, | 
|  | StudyConfiguration{2, 3, SizeRange{4, 5, 6}, Align(8), 9, 10}, | 
|  | {FunctionMeasurements{"A", | 
|  | {Measurement{3, std::chrono::seconds(3)}, | 
|  | Measurement{3, std::chrono::seconds(4)}}}, | 
|  | FunctionMeasurements{"B", {}}}}; | 
|  | } | 
|  |  | 
|  | 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 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 SizeRange &SR) -> auto { | 
|  | return AllOf(Field(&SizeRange::From, SR.From), Field(&SizeRange::To, SR.To), | 
|  | Field(&SizeRange::Step, SR.Step)); | 
|  | } | 
|  |  | 
|  | auto Equals(const StudyConfiguration &SC) -> auto { | 
|  | return AllOf( | 
|  | Field(&StudyConfiguration::Runs, SC.Runs), | 
|  | Field(&StudyConfiguration::BufferSize, SC.BufferSize), | 
|  | Field(&StudyConfiguration::Size, Equals(SC.Size)), | 
|  | Field(&StudyConfiguration::AddressAlignment, SC.AddressAlignment), | 
|  | Field(&StudyConfiguration::MemsetValue, SC.MemsetValue), | 
|  | Field(&StudyConfiguration::MemcmpMismatchAt, SC.MemcmpMismatchAt)); | 
|  | } | 
|  |  | 
|  | MATCHER(EqualsMeasurement, "") { | 
|  | const Measurement &A = ::testing::get<0>(arg); | 
|  | const Measurement &B = ::testing::get<1>(arg); | 
|  | return ExplainMatchResult(AllOf(Field(&Measurement::Size, B.Size), | 
|  | Field(&Measurement::Runtime, B.Runtime)), | 
|  | A, result_listener); | 
|  | } | 
|  |  | 
|  | MATCHER(EqualsFunctions, "") { | 
|  | const FunctionMeasurements &A = ::testing::get<0>(arg); | 
|  | const FunctionMeasurements &B = ::testing::get<1>(arg); | 
|  | return ExplainMatchResult( | 
|  | AllOf(Field(&FunctionMeasurements::Name, B.Name), | 
|  | Field(&FunctionMeasurements::Measurements, | 
|  | Pointwise(EqualsMeasurement(), B.Measurements))), | 
|  | A, result_listener); | 
|  | } | 
|  |  | 
|  | auto Equals(const Study &S) -> auto { | 
|  | return AllOf( | 
|  | Field(&Study::Host, Equals(S.Host)), | 
|  | Field(&Study::Options, Equals(S.Options)), | 
|  | Field(&Study::Configuration, Equals(S.Configuration)), | 
|  | Field(&Study::Functions, Pointwise(EqualsFunctions(), S.Functions))); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, RoundTrip) { | 
|  | const Study S = getStudy(); | 
|  | auto StudyOrError = ParseJsonStudy(SerializeToString(S)); | 
|  | if (auto Err = StudyOrError.takeError()) | 
|  | EXPECT_FALSE(Err) << "Unexpected error"; | 
|  | const Study &Parsed = *StudyOrError; | 
|  | EXPECT_THAT(Parsed, Equals(S)); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, SupplementaryField) { | 
|  | auto Failure = ParseJsonStudy(R"({ | 
|  | "UnknownField": 10 | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Unknown field: UnknownField"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidType) { | 
|  | auto Failure = ParseJsonStudy(R"({ | 
|  | "Options": 1 | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Expected JSON Object"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidDuration) { | 
|  | auto Failure = ParseJsonStudy(R"({ | 
|  | "Options": { | 
|  | "MinDuration": "Duration should be a Number" | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), "Can't parse Duration"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidAlignType) { | 
|  | auto Failure = ParseJsonStudy(R"({ | 
|  | "Configuration":{ | 
|  | "AddressAlignment": "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":{ | 
|  | "AddressAlignment":3 | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), | 
|  | "Can't parse Align, not a power of two"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidBenchmarkLogType) { | 
|  | auto Failure = ParseJsonStudy(R"({ | 
|  | "Options":{ | 
|  | "Log": 3 | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), | 
|  | "Can't parse BenchmarkLog, not a String"); | 
|  | } | 
|  |  | 
|  | TEST(JsonTest, InvalidBenchmarkLog) { | 
|  | auto Failure = ParseJsonStudy(R"({ | 
|  | "Options":{ | 
|  | "Log": "Unknown" | 
|  | } | 
|  | } | 
|  | )"); | 
|  | EXPECT_EQ(toString(Failure.takeError()), | 
|  | "Can't parse BenchmarkLog, invalid value 'Unknown'"); | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  | } // namespace libc_benchmarks | 
|  | } // namespace llvm |