|  | //===- unittest/Format/FormatTestJson.cpp - Formatting tests for Json     -===// | 
|  | // | 
|  | // 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 "FormatTestUtils.h" | 
|  | #include "clang/Format/Format.h" | 
|  | #include "llvm/Support/Debug.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | #define DEBUG_TYPE "format-test-json" | 
|  |  | 
|  | namespace clang { | 
|  | namespace format { | 
|  |  | 
|  | class FormatTestJson : public testing::Test { | 
|  | protected: | 
|  | static std::string format(StringRef Code, unsigned Offset, unsigned Length, | 
|  | const FormatStyle &Style) { | 
|  | LLVM_DEBUG(llvm::errs() << "---\n"); | 
|  | LLVM_DEBUG(llvm::errs() << Code << "\n\n"); | 
|  |  | 
|  | tooling::Replacements Replaces; | 
|  |  | 
|  | // Mock up what ClangFormat.cpp will do for JSON by adding a variable | 
|  | // to trick JSON into being JavaScript | 
|  | if (Style.isJson() && !Style.DisableFormat) { | 
|  | auto Err = Replaces.add( | 
|  | tooling::Replacement(tooling::Replacement("", 0, 0, "x = "))); | 
|  | if (Err) | 
|  | llvm::errs() << "Bad Json variable insertion\n"; | 
|  | } | 
|  | auto ChangedCode = applyAllReplacements(Code, Replaces); | 
|  | if (!ChangedCode) | 
|  | llvm::errs() << "Bad Json varibale replacement\n"; | 
|  | StringRef NewCode = *ChangedCode; | 
|  |  | 
|  | std::vector<tooling::Range> Ranges(1, tooling::Range(0, NewCode.size())); | 
|  | Replaces = reformat(Style, NewCode, Ranges); | 
|  | auto Result = applyAllReplacements(NewCode, Replaces); | 
|  | EXPECT_TRUE(static_cast<bool>(Result)); | 
|  | LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); | 
|  | return *Result; | 
|  | } | 
|  |  | 
|  | static std::string | 
|  | format(StringRef Code, | 
|  | const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Json)) { | 
|  | return format(Code, 0, Code.size(), Style); | 
|  | } | 
|  |  | 
|  | static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { | 
|  | FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); | 
|  | Style.ColumnLimit = ColumnLimit; | 
|  | return Style; | 
|  | } | 
|  |  | 
|  | static void verifyFormatStable(StringRef Code, const FormatStyle &Style) { | 
|  | EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; | 
|  | } | 
|  |  | 
|  | static void | 
|  | verifyFormat(StringRef Code, | 
|  | const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Json)) { | 
|  | verifyFormatStable(Code, Style); | 
|  | EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(FormatTestJson, JsonRecord) { | 
|  | verifyFormat("{}"); | 
|  | verifyFormat("{\n" | 
|  | "  \"name\": 1\n" | 
|  | "}"); | 
|  | verifyFormat("{\n" | 
|  | "  \"name\": \"Foo\"\n" | 
|  | "}"); | 
|  | verifyFormat("{\n" | 
|  | "  \"name\": {\n" | 
|  | "    \"value\": 1\n" | 
|  | "  }\n" | 
|  | "}"); | 
|  | verifyFormat("{\n" | 
|  | "  \"name\": {\n" | 
|  | "    \"value\": 1\n" | 
|  | "  },\n" | 
|  | "  \"name\": {\n" | 
|  | "    \"value\": 2\n" | 
|  | "  }\n" | 
|  | "}"); | 
|  | verifyFormat("{\n" | 
|  | "  \"name\": {\n" | 
|  | "    \"value\": [\n" | 
|  | "      1,\n" | 
|  | "      2,\n" | 
|  | "    ]\n" | 
|  | "  }\n" | 
|  | "}"); | 
|  | verifyFormat("{\n" | 
|  | "  \"name\": {\n" | 
|  | "    \"value\": [\n" | 
|  | "      \"name\": {\n" | 
|  | "        \"value\": 1\n" | 
|  | "      },\n" | 
|  | "      \"name\": {\n" | 
|  | "        \"value\": 2\n" | 
|  | "      }\n" | 
|  | "    ]\n" | 
|  | "  }\n" | 
|  | "}"); | 
|  | verifyFormat(R"({ | 
|  | "firstName": "John", | 
|  | "lastName": "Smith", | 
|  | "isAlive": true, | 
|  | "age": 27, | 
|  | "address": { | 
|  | "streetAddress": "21 2nd Street", | 
|  | "city": "New York", | 
|  | "state": "NY", | 
|  | "postalCode": "10021-3100" | 
|  | }, | 
|  | "phoneNumbers": [ | 
|  | { | 
|  | "type": "home", | 
|  | "number": "212 555-1234" | 
|  | }, | 
|  | { | 
|  | "type": "office", | 
|  | "number": "646 555-4567" | 
|  | } | 
|  | ], | 
|  | "children": [], | 
|  | "spouse": null | 
|  | })"); | 
|  | } | 
|  |  | 
|  | TEST_F(FormatTestJson, JsonArray) { | 
|  | verifyFormat("[]"); | 
|  | verifyFormat("[\n" | 
|  | "  1\n" | 
|  | "]"); | 
|  | verifyFormat("[\n" | 
|  | "  1,\n" | 
|  | "  2\n" | 
|  | "]"); | 
|  | verifyFormat("[\n" | 
|  | "  {},\n" | 
|  | "  {}\n" | 
|  | "]"); | 
|  | verifyFormat("[\n" | 
|  | "  {\n" | 
|  | "    \"name\": 1\n" | 
|  | "  },\n" | 
|  | "  {}\n" | 
|  | "]"); | 
|  | } | 
|  |  | 
|  | TEST_F(FormatTestJson, JsonArrayOneLine) { | 
|  | FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); | 
|  | Style.BreakArrays = false; | 
|  | Style.SpacesInContainerLiterals = false; | 
|  | verifyFormat("[]", Style); | 
|  | verifyFormat("[1]", Style); | 
|  | verifyFormat("[1, 2]", Style); | 
|  | verifyFormat("[1, 2, 3]", Style); | 
|  | verifyFormat("[1, 2, 3, 4]", Style); | 
|  | verifyFormat("[1, 2, 3, 4, 5]", Style); | 
|  |  | 
|  | verifyFormat("[\n" | 
|  | "  1,\n" | 
|  | "  2,\n" | 
|  | "  {\n" | 
|  | "    A: 1\n" | 
|  | "  }\n" | 
|  | "]", | 
|  | Style); | 
|  | } | 
|  |  | 
|  | TEST_F(FormatTestJson, JsonNoStringSplit) { | 
|  | FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); | 
|  | Style.IndentWidth = 4; | 
|  | verifyFormat( | 
|  | "[\n" | 
|  | "    {\n" | 
|  | "        " | 
|  | "\"naaaaaaaa\": \"foooooooooooooooooooooo oooooooooooooooooooooo\"\n" | 
|  | "    },\n" | 
|  | "    {}\n" | 
|  | "]", | 
|  | Style); | 
|  | verifyFormat("[\n" | 
|  | "    {\n" | 
|  | "        " | 
|  | "\"naaaaaaaa\": " | 
|  | "\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" | 
|  | "oooooooooooooooooooooooooo\"\n" | 
|  | "    },\n" | 
|  | "    {}\n" | 
|  | "]", | 
|  | Style); | 
|  |  | 
|  | Style.ColumnLimit = 80; | 
|  | verifyFormat("[\n" | 
|  | "    {\n" | 
|  | "        " | 
|  | "\"naaaaaaaa\":\n" | 
|  | "            " | 
|  | "\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" | 
|  | "oooooooooooooooooooooooooo\"\n" | 
|  | "    },\n" | 
|  | "    {}\n" | 
|  | "]", | 
|  | Style); | 
|  | } | 
|  |  | 
|  | TEST_F(FormatTestJson, DisableJsonFormat) { | 
|  | FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); | 
|  | verifyFormatStable("{}", Style); | 
|  | verifyFormatStable("{\n" | 
|  | "  \"name\": 1\n" | 
|  | "}", | 
|  | Style); | 
|  |  | 
|  | // Since we have to disable formatting to run this test, we shall refrain from | 
|  | // calling test::messUp lest we change the unformatted code and cannot format | 
|  | // it back to how it started. | 
|  | Style.DisableFormat = true; | 
|  | verifyFormatStable("{}", Style); | 
|  | verifyFormatStable("{\n" | 
|  | "  \"name\": 1\n" | 
|  | "}", | 
|  | Style); | 
|  | } | 
|  |  | 
|  | TEST_F(FormatTestJson, SpaceBeforeJsonColon) { | 
|  | FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); | 
|  | verifyFormatStable("{\n" | 
|  | "  \"name\": 1\n" | 
|  | "}", | 
|  | Style); | 
|  |  | 
|  | Style.SpaceBeforeJsonColon = true; | 
|  | verifyFormatStable("{}", Style); | 
|  | verifyFormatStable("{\n" | 
|  | "  \"name\" : 1\n" | 
|  | "}", | 
|  | Style); | 
|  | } | 
|  |  | 
|  | TEST_F(FormatTestJson, StartsWithWhitespaces) { | 
|  | FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); | 
|  | EXPECT_EQ("{\n" | 
|  | "  \"name\": 1\n" | 
|  | "}", | 
|  | format(" {\n" | 
|  | "  \"name\": 1\n" | 
|  | "}", | 
|  | Style)); | 
|  |  | 
|  | // FIXME: The block below is over-indented. | 
|  | EXPECT_EQ("    {\n" | 
|  | "      \"name\": 1\n" | 
|  | "    }", | 
|  | format("\n{\n" | 
|  | "  \"name\": 1\n" | 
|  | "}", | 
|  | Style)); | 
|  | } | 
|  |  | 
|  | } // namespace format | 
|  | } // end namespace clang |