1//===-- JSON Tests --------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "JSON.h"
10#include "LibcBenchmark.h"
11#include "LibcMemoryBenchmark.h"
12#include "llvm/Support/JSON.h"
13#include "llvm/Support/raw_ostream.h"
14#include "gmock/gmock.h"
15#include "gtest/gtest.h"
16
17using testing::AllOf;
18using testing::ExplainMatchResult;
19using testing::Field;
20using testing::Pointwise;
21
22namespace llvm {
23namespace libc_benchmarks {
24namespace {
25
26Study getStudy() {
27 return Study{
28 .StudyName: "StudyName",
29 .Runtime: Runtime{.Host: HostState{.CpuName: "CpuName",
30 .CpuFrequency: 123,
31 .Caches: {CacheInfo{.Type: "A", .Level: 1, .Size: 2, .NumSharing: 3}, CacheInfo{.Type: "B", .Level: 4, .Size: 5, .NumSharing: 6}}},
32 .BufferSize: 456, .BatchParameterCount: 789,
33 .BenchmarkOptions: BenchmarkOptions{.MinDuration: std::chrono::seconds(1), .MaxDuration: std::chrono::seconds(2),
34 .InitialIterations: 10, .MaxIterations: 100, .MinSamples: 6, .MaxSamples: 100, .Epsilon: 0.1, .ScalingFactor: 2, .Log: BenchmarkLog::Full}},
35 .Configuration: StudyConfiguration{.Function: std::string("Function"), .NumTrials: 30U, .IsSweepMode: false, .SweepModeMaxSize: 32U,
36 .SizeDistributionName: std::string("Distribution"), .AccessAlignment: Align(16), .MemcmpMismatchAt: 3U},
37 .Measurements: {std::chrono::seconds(3), std::chrono::seconds(4)}};
38}
39
40static std::string serializeToString(const Study &S) {
41 std::string Buffer;
42 raw_string_ostream RSO(Buffer);
43 json::OStream JOS(RSO);
44 serializeToJson(S, JOS);
45 return Buffer;
46}
47
48MATCHER(EqualsCacheInfo, "") {
49 const CacheInfo &A = ::testing::get<0>(arg);
50 const CacheInfo &B = ::testing::get<1>(arg);
51 return ExplainMatchResult(matcher: AllOf(matchers: Field(field: &CacheInfo::Type, matcher: B.Type),
52 matchers: Field(field: &CacheInfo::Level, matcher: B.Level),
53 matchers: Field(field: &CacheInfo::Size, matcher: B.Size),
54 matchers: Field(field: &CacheInfo::NumSharing, matcher: B.NumSharing)),
55 value: A, listener: result_listener);
56}
57
58auto equals(const HostState &H) -> auto {
59 return AllOf(
60 matchers: Field(field: &HostState::CpuName, matcher: H.CpuName),
61 matchers: Field(field: &HostState::CpuFrequency, matcher: H.CpuFrequency),
62 matchers: Field(field: &HostState::Caches, matcher: Pointwise(tuple_matcher: EqualsCacheInfo(), rhs: H.Caches)));
63}
64
65auto equals(const StudyConfiguration &SC) -> auto {
66 return AllOf(
67 matchers: Field(field: &StudyConfiguration::Function, matcher: SC.Function),
68 matchers: Field(field: &StudyConfiguration::NumTrials, matcher: SC.NumTrials),
69 matchers: Field(field: &StudyConfiguration::IsSweepMode, matcher: SC.IsSweepMode),
70 matchers: Field(field: &StudyConfiguration::SweepModeMaxSize, matcher: SC.SweepModeMaxSize),
71 matchers: Field(field: &StudyConfiguration::SizeDistributionName, matcher: SC.SizeDistributionName),
72 matchers: Field(field: &StudyConfiguration::AccessAlignment, matcher: SC.AccessAlignment),
73 matchers: Field(field: &StudyConfiguration::MemcmpMismatchAt, matcher: SC.MemcmpMismatchAt));
74}
75
76auto equals(const BenchmarkOptions &BO) -> auto {
77 return AllOf(
78 matchers: Field(field: &BenchmarkOptions::MinDuration, matcher: BO.MinDuration),
79 matchers: Field(field: &BenchmarkOptions::MaxDuration, matcher: BO.MaxDuration),
80 matchers: Field(field: &BenchmarkOptions::InitialIterations, matcher: BO.InitialIterations),
81 matchers: Field(field: &BenchmarkOptions::MaxIterations, matcher: BO.MaxIterations),
82 matchers: Field(field: &BenchmarkOptions::MinSamples, matcher: BO.MinSamples),
83 matchers: Field(field: &BenchmarkOptions::MaxSamples, matcher: BO.MaxSamples),
84 matchers: Field(field: &BenchmarkOptions::Epsilon, matcher: BO.Epsilon),
85 matchers: Field(field: &BenchmarkOptions::ScalingFactor, matcher: BO.ScalingFactor),
86 matchers: Field(field: &BenchmarkOptions::Log, matcher: BO.Log));
87}
88
89auto equals(const Runtime &RI) -> auto {
90 return AllOf(matchers: Field(field: &Runtime::Host, matcher: equals(H: RI.Host)),
91 matchers: Field(field: &Runtime::BufferSize, matcher: RI.BufferSize),
92 matchers: Field(field: &Runtime::BatchParameterCount, matcher: RI.BatchParameterCount),
93 matchers: Field(field: &Runtime::BenchmarkOptions, matcher: equals(BO: RI.BenchmarkOptions)));
94}
95
96auto equals(const Study &S) -> auto {
97 return AllOf(matchers: Field(field: &Study::StudyName, matcher: S.StudyName),
98 matchers: Field(field: &Study::Runtime, matcher: equals(RI: S.Runtime)),
99 matchers: Field(field: &Study::Configuration, matcher: equals(SC: S.Configuration)),
100 matchers: Field(field: &Study::Measurements, matcher: S.Measurements));
101}
102
103TEST(JsonTest, RoundTrip) {
104 const Study S = getStudy();
105 const auto Serialized = serializeToString(S);
106 auto StudyOrError = parseJsonStudy(Content: Serialized);
107 if (auto Err = StudyOrError.takeError())
108 EXPECT_FALSE(Err) << "Unexpected error : " << Err << "\n" << Serialized;
109 const Study &Parsed = *StudyOrError;
110 EXPECT_THAT(Parsed, equals(S)) << Serialized << "\n"
111 << serializeToString(S: Parsed);
112}
113
114TEST(JsonTest, SupplementaryField) {
115 auto Failure = parseJsonStudy(Content: R"({
116 "UnknownField": 10
117 }
118 )");
119 EXPECT_EQ(toString(Failure.takeError()), "Unknown field: UnknownField");
120}
121
122TEST(JsonTest, InvalidType) {
123 auto Failure = parseJsonStudy(Content: R"({
124 "Runtime": 1
125 }
126 )");
127 EXPECT_EQ(toString(Failure.takeError()), "Expected JSON Object");
128}
129
130TEST(JsonTest, InvalidDuration) {
131 auto Failure = parseJsonStudy(Content: R"({
132 "Runtime": {
133 "BenchmarkOptions": {
134 "MinDuration": "Duration should be a Number"
135 }
136 }
137 }
138 )");
139 EXPECT_EQ(toString(Failure.takeError()), "Can't parse Duration");
140}
141
142TEST(JsonTest, InvalidAlignType) {
143 auto Failure = parseJsonStudy(Content: R"({
144 "Configuration": {
145 "AccessAlignment": "Align should be an Integer"
146 }
147 }
148 )");
149 EXPECT_EQ(toString(Failure.takeError()), "Can't parse Align, not an Integer");
150}
151
152TEST(JsonTest, InvalidAlign) {
153 auto Failure = parseJsonStudy(Content: R"({
154 "Configuration": {
155 "AccessAlignment": 3
156 }
157 }
158 )");
159 EXPECT_EQ(toString(Failure.takeError()),
160 "Can't parse Align, not a power of two");
161}
162
163TEST(JsonTest, InvalidBenchmarkLogType) {
164 auto Failure = parseJsonStudy(Content: R"({
165 "Runtime": {
166 "BenchmarkOptions":{
167 "Log": 3
168 }
169 }
170 }
171 )");
172 EXPECT_EQ(toString(Failure.takeError()),
173 "Can't parse BenchmarkLog, not a String");
174}
175
176TEST(JsonTest, InvalidBenchmarkLog) {
177 auto Failure = parseJsonStudy(Content: R"({
178 "Runtime": {
179 "BenchmarkOptions":{
180 "Log": "Unknown"
181 }
182 }
183 }
184 )");
185 EXPECT_EQ(toString(Failure.takeError()),
186 "Can't parse BenchmarkLog, invalid value 'Unknown'");
187}
188
189} // namespace
190} // namespace libc_benchmarks
191} // namespace llvm
192

source code of libc/benchmarks/JSONTest.cpp