| 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: |
| 21 | static std::string format(StringRef Code, unsigned Offset, unsigned Length, |
| 22 | 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 | R: 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, Code: NewCode, Ranges); |
| 43 | auto Result = applyAllReplacements(Code: 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 |
| 50 | format(StringRef Code, |
| 51 | const FormatStyle &Style = getLLVMStyle(Language: FormatStyle::LK_Json)) { |
| 52 | return format(Code, Offset: 0, Length: Code.size(), Style); |
| 53 | } |
| 54 | |
| 55 | static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { |
| 56 | FormatStyle Style = getLLVMStyle(Language: FormatStyle::LK_Json); |
| 57 | Style.ColumnLimit = ColumnLimit; |
| 58 | return Style; |
| 59 | } |
| 60 | |
| 61 | static void verifyFormatStable(StringRef Code, const FormatStyle &Style) { |
| 62 | EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable" ; |
| 63 | } |
| 64 | |
| 65 | static void |
| 66 | verifyFormat(StringRef Code, |
| 67 | const FormatStyle &Style = getLLVMStyle(Language: FormatStyle::LK_Json)) { |
| 68 | verifyFormatStable(Code, Style); |
| 69 | EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); |
| 70 | } |
| 71 | }; |
| 72 | |
| 73 | TEST_F(FormatTestJson, JsonRecord) { |
| 74 | verifyFormat(Code: "{}" ); |
| 75 | verifyFormat(Code: "{\n" |
| 76 | " \"name\": 1\n" |
| 77 | "}" ); |
| 78 | verifyFormat(Code: "{\n" |
| 79 | " \"name\": \"Foo\"\n" |
| 80 | "}" ); |
| 81 | verifyFormat(Code: "{\n" |
| 82 | " \"name\": {\n" |
| 83 | " \"value\": 1\n" |
| 84 | " }\n" |
| 85 | "}" ); |
| 86 | verifyFormat(Code: "{\n" |
| 87 | " \"name\": {\n" |
| 88 | " \"value\": 1\n" |
| 89 | " },\n" |
| 90 | " \"name\": {\n" |
| 91 | " \"value\": 2\n" |
| 92 | " }\n" |
| 93 | "}" ); |
| 94 | verifyFormat(Code: "{\n" |
| 95 | " \"name\": {\n" |
| 96 | " \"value\": [\n" |
| 97 | " 1,\n" |
| 98 | " 2,\n" |
| 99 | " ]\n" |
| 100 | " }\n" |
| 101 | "}" ); |
| 102 | verifyFormat(Code: "{\n" |
| 103 | " \"name\": {\n" |
| 104 | " \"value\": [\n" |
| 105 | " \"name\": {\n" |
| 106 | " \"value\": 1\n" |
| 107 | " },\n" |
| 108 | " \"name\": {\n" |
| 109 | " \"value\": 2\n" |
| 110 | " }\n" |
| 111 | " ]\n" |
| 112 | " }\n" |
| 113 | "}" ); |
| 114 | verifyFormat(Code: R"({ |
| 115 | "firstName": "John", |
| 116 | "lastName": "Smith", |
| 117 | "isAlive": true, |
| 118 | "age": 27, |
| 119 | "address": { |
| 120 | "streetAddress": "21 2nd Street", |
| 121 | "city": "New York", |
| 122 | "state": "NY", |
| 123 | "postalCode": "10021-3100" |
| 124 | }, |
| 125 | "phoneNumbers": [ |
| 126 | { |
| 127 | "type": "home", |
| 128 | "number": "212 555-1234" |
| 129 | }, |
| 130 | { |
| 131 | "type": "office", |
| 132 | "number": "646 555-4567" |
| 133 | } |
| 134 | ], |
| 135 | "children": [], |
| 136 | "spouse": null |
| 137 | })" ); |
| 138 | } |
| 139 | |
| 140 | TEST_F(FormatTestJson, JsonArray) { |
| 141 | verifyFormat(Code: "[]" ); |
| 142 | verifyFormat(Code: "[\n" |
| 143 | " 1\n" |
| 144 | "]" ); |
| 145 | verifyFormat(Code: "[\n" |
| 146 | " 1,\n" |
| 147 | " 2\n" |
| 148 | "]" ); |
| 149 | verifyFormat(Code: "[\n" |
| 150 | " {},\n" |
| 151 | " {}\n" |
| 152 | "]" ); |
| 153 | verifyFormat(Code: "[\n" |
| 154 | " {\n" |
| 155 | " \"name\": 1\n" |
| 156 | " },\n" |
| 157 | " {}\n" |
| 158 | "]" ); |
| 159 | } |
| 160 | |
| 161 | TEST_F(FormatTestJson, JsonArrayOneLine) { |
| 162 | FormatStyle Style = getLLVMStyle(Language: FormatStyle::LK_Json); |
| 163 | Style.BreakArrays = false; |
| 164 | Style.SpacesInContainerLiterals = false; |
| 165 | verifyFormat(Code: "[]" , Style); |
| 166 | verifyFormat(Code: "[1]" , Style); |
| 167 | verifyFormat(Code: "[1, 2]" , Style); |
| 168 | verifyFormat(Code: "[1, 2, 3]" , Style); |
| 169 | verifyFormat(Code: "[1, 2, 3, 4]" , Style); |
| 170 | verifyFormat(Code: "[1, 2, 3, 4, 5]" , Style); |
| 171 | |
| 172 | verifyFormat(Code: "[\n" |
| 173 | " 1,\n" |
| 174 | " 2,\n" |
| 175 | " {\n" |
| 176 | " A: 1\n" |
| 177 | " }\n" |
| 178 | "]" , |
| 179 | Style); |
| 180 | } |
| 181 | |
| 182 | TEST_F(FormatTestJson, JsonNoStringSplit) { |
| 183 | FormatStyle Style = getLLVMStyle(Language: FormatStyle::LK_Json); |
| 184 | Style.IndentWidth = 4; |
| 185 | verifyFormat( |
| 186 | Code: "[\n" |
| 187 | " {\n" |
| 188 | " " |
| 189 | "\"naaaaaaaa\": \"foooooooooooooooooooooo oooooooooooooooooooooo\"\n" |
| 190 | " },\n" |
| 191 | " {}\n" |
| 192 | "]" , |
| 193 | Style); |
| 194 | verifyFormat(Code: "[\n" |
| 195 | " {\n" |
| 196 | " " |
| 197 | "\"naaaaaaaa\": " |
| 198 | "\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" |
| 199 | "oooooooooooooooooooooooooo\"\n" |
| 200 | " },\n" |
| 201 | " {}\n" |
| 202 | "]" , |
| 203 | Style); |
| 204 | |
| 205 | Style.ColumnLimit = 80; |
| 206 | verifyFormat(Code: "[\n" |
| 207 | " {\n" |
| 208 | " " |
| 209 | "\"naaaaaaaa\":\n" |
| 210 | " " |
| 211 | "\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" |
| 212 | "oooooooooooooooooooooooooo\"\n" |
| 213 | " },\n" |
| 214 | " {}\n" |
| 215 | "]" , |
| 216 | Style); |
| 217 | } |
| 218 | |
| 219 | TEST_F(FormatTestJson, DisableJsonFormat) { |
| 220 | FormatStyle Style = getLLVMStyle(Language: FormatStyle::LK_Json); |
| 221 | verifyFormatStable(Code: "{}" , Style); |
| 222 | verifyFormatStable(Code: "{\n" |
| 223 | " \"name\": 1\n" |
| 224 | "}" , |
| 225 | Style); |
| 226 | |
| 227 | // Since we have to disable formatting to run this test, we shall refrain from |
| 228 | // calling test::messUp lest we change the unformatted code and cannot format |
| 229 | // it back to how it started. |
| 230 | Style.DisableFormat = true; |
| 231 | verifyFormatStable(Code: "{}" , Style); |
| 232 | verifyFormatStable(Code: "{\n" |
| 233 | " \"name\": 1\n" |
| 234 | "}" , |
| 235 | Style); |
| 236 | } |
| 237 | |
| 238 | TEST_F(FormatTestJson, SpaceBeforeJsonColon) { |
| 239 | FormatStyle Style = getLLVMStyle(Language: FormatStyle::LK_Json); |
| 240 | verifyFormatStable(Code: "{\n" |
| 241 | " \"name\": 1\n" |
| 242 | "}" , |
| 243 | Style); |
| 244 | |
| 245 | Style.SpaceBeforeJsonColon = true; |
| 246 | verifyFormatStable(Code: "{}" , Style); |
| 247 | verifyFormatStable(Code: "{\n" |
| 248 | " \"name\" : 1\n" |
| 249 | "}" , |
| 250 | Style); |
| 251 | } |
| 252 | |
| 253 | TEST_F(FormatTestJson, StartsWithWhitespaces) { |
| 254 | FormatStyle Style = getLLVMStyle(Language: FormatStyle::LK_Json); |
| 255 | EXPECT_EQ("{\n" |
| 256 | " \"name\": 1\n" |
| 257 | "}" , |
| 258 | format(" {\n" |
| 259 | " \"name\": 1\n" |
| 260 | "}" , |
| 261 | Style)); |
| 262 | |
| 263 | // FIXME: The block below is over-indented. |
| 264 | EXPECT_EQ(" {\n" |
| 265 | " \"name\": 1\n" |
| 266 | " }" , |
| 267 | format("\n{\n" |
| 268 | " \"name\": 1\n" |
| 269 | "}" , |
| 270 | Style)); |
| 271 | } |
| 272 | |
| 273 | } // namespace format |
| 274 | } // end namespace clang |
| 275 | |