1 //===- unittest/Format/FormatTestJson.cpp - Formatting tests for Json     -===//
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 "FormatTestUtils.h"
10 #include "clang/Format/Format.h"
11 #include "llvm/Support/Debug.h"
12 #include "gtest/gtest.h"
13 
14 #define DEBUG_TYPE "format-test-json"
15 
16 namespace clang {
17 namespace format {
18 
19 class FormatTestJson : public ::testing::Test {
20 protected:
format(llvm::StringRef Code,unsigned Offset,unsigned Length,const FormatStyle & Style)21   static std::string format(llvm::StringRef Code, unsigned Offset,
22                             unsigned Length, const FormatStyle &Style) {
23     LLVM_DEBUG(llvm::errs() << "---\n");
24     LLVM_DEBUG(llvm::errs() << Code << "\n\n");
25 
26     tooling::Replacements Replaces;
27 
28     // Mock up what ClangFormat.cpp will do for JSON by adding a variable
29     // to trick JSON into being JavaScript
30     if (Style.isJson() && !Style.DisableFormat) {
31       auto Err = Replaces.add(
32           tooling::Replacement(tooling::Replacement("", 0, 0, "x = ")));
33       if (Err)
34         llvm::errs() << "Bad Json variable insertion\n";
35     }
36     auto ChangedCode = applyAllReplacements(Code, Replaces);
37     if (!ChangedCode)
38       llvm::errs() << "Bad Json varibale replacement\n";
39     StringRef NewCode = *ChangedCode;
40 
41     std::vector<tooling::Range> Ranges(1, tooling::Range(0, NewCode.size()));
42     Replaces = reformat(Style, NewCode, Ranges);
43     auto Result = applyAllReplacements(NewCode, Replaces);
44     EXPECT_TRUE(static_cast<bool>(Result));
45     LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
46     return *Result;
47   }
48 
49   static std::string
format(llvm::StringRef Code,const FormatStyle & Style=getLLVMStyle (FormatStyle::LK_Json))50   format(llvm::StringRef Code,
51          const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Json)) {
52     return format(Code, 0, Code.size(), Style);
53   }
54 
getStyleWithColumns(unsigned ColumnLimit)55   static FormatStyle getStyleWithColumns(unsigned ColumnLimit) {
56     FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json);
57     Style.ColumnLimit = ColumnLimit;
58     return Style;
59   }
60 
verifyFormatStable(llvm::StringRef Code,const FormatStyle & Style)61   static void verifyFormatStable(llvm::StringRef Code,
62                                  const FormatStyle &Style) {
63     EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable";
64   }
65 
66   static void
verifyFormat(llvm::StringRef Code,const FormatStyle & Style=getLLVMStyle (FormatStyle::LK_Json))67   verifyFormat(llvm::StringRef Code,
68                const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Json)) {
69     verifyFormatStable(Code, Style);
70     EXPECT_EQ(Code.str(), format(test::messUp(Code), Style));
71   }
72 };
73 
TEST_F(FormatTestJson,JsonRecord)74 TEST_F(FormatTestJson, JsonRecord) {
75   verifyFormat("{}");
76   verifyFormat("{\n"
77                "  \"name\": 1\n"
78                "}");
79   verifyFormat("{\n"
80                "  \"name\": \"Foo\"\n"
81                "}");
82   verifyFormat("{\n"
83                "  \"name\": {\n"
84                "    \"value\": 1\n"
85                "  }\n"
86                "}");
87   verifyFormat("{\n"
88                "  \"name\": {\n"
89                "    \"value\": 1\n"
90                "  },\n"
91                "  \"name\": {\n"
92                "    \"value\": 2\n"
93                "  }\n"
94                "}");
95   verifyFormat("{\n"
96                "  \"name\": {\n"
97                "    \"value\": [\n"
98                "      1,\n"
99                "      2,\n"
100                "    ]\n"
101                "  }\n"
102                "}");
103   verifyFormat("{\n"
104                "  \"name\": {\n"
105                "    \"value\": [\n"
106                "      \"name\": {\n"
107                "        \"value\": 1\n"
108                "      },\n"
109                "      \"name\": {\n"
110                "        \"value\": 2\n"
111                "      }\n"
112                "    ]\n"
113                "  }\n"
114                "}");
115   verifyFormat(R"({
116   "firstName": "John",
117   "lastName": "Smith",
118   "isAlive": true,
119   "age": 27,
120   "address": {
121     "streetAddress": "21 2nd Street",
122     "city": "New York",
123     "state": "NY",
124     "postalCode": "10021-3100"
125   },
126   "phoneNumbers": [
127     {
128       "type": "home",
129       "number": "212 555-1234"
130     },
131     {
132       "type": "office",
133       "number": "646 555-4567"
134     }
135   ],
136   "children": [],
137   "spouse": null
138 })");
139 }
140 
TEST_F(FormatTestJson,JsonArray)141 TEST_F(FormatTestJson, JsonArray) {
142   verifyFormat("[]");
143   verifyFormat("[\n"
144                "  1\n"
145                "]");
146   verifyFormat("[\n"
147                "  1,\n"
148                "  2\n"
149                "]");
150   verifyFormat("[\n"
151                "  {},\n"
152                "  {}\n"
153                "]");
154   verifyFormat("[\n"
155                "  {\n"
156                "    \"name\": 1\n"
157                "  },\n"
158                "  {}\n"
159                "]");
160 }
161 
TEST_F(FormatTestJson,JsonNoStringSplit)162 TEST_F(FormatTestJson, JsonNoStringSplit) {
163   FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json);
164   Style.IndentWidth = 4;
165   verifyFormat(
166       "[\n"
167       "    {\n"
168       "        "
169       "\"naaaaaaaa\": \"foooooooooooooooooooooo oooooooooooooooooooooo\"\n"
170       "    },\n"
171       "    {}\n"
172       "]",
173       Style);
174   verifyFormat("[\n"
175                "    {\n"
176                "        "
177                "\"naaaaaaaa\": "
178                "\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
179                "oooooooooooooooooooooooooo\"\n"
180                "    },\n"
181                "    {}\n"
182                "]",
183                Style);
184 
185   Style.ColumnLimit = 80;
186   verifyFormat("[\n"
187                "    {\n"
188                "        "
189                "\"naaaaaaaa\":\n"
190                "            "
191                "\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
192                "oooooooooooooooooooooooooo\"\n"
193                "    },\n"
194                "    {}\n"
195                "]",
196                Style);
197 }
198 
TEST_F(FormatTestJson,DisableJsonFormat)199 TEST_F(FormatTestJson, DisableJsonFormat) {
200   FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json);
201   verifyFormatStable("{}", Style);
202   verifyFormatStable("{\n"
203                      "  \"name\": 1\n"
204                      "}",
205                      Style);
206 
207   // Since we have to disable formatting to run this test, we shall refrain from
208   // calling test::messUp lest we change the unformatted code and cannot format
209   // it back to how it started.
210   Style.DisableFormat = true;
211   verifyFormatStable("{}", Style);
212   verifyFormatStable("{\n"
213                      "  \"name\": 1\n"
214                      "}",
215                      Style);
216 }
217 
218 } // namespace format
219 } // end namespace clang
220