//===- unittest/Format/FormatTestTextProto.cpp ----------------------------===// // // 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" namespace clang { namespace format { class FormatTestTextProto : public ::testing::Test { protected: static std::string format(llvm::StringRef Code, unsigned Offset, unsigned Length, const FormatStyle &Style) { LLVM_DEBUG(llvm::errs() << "---\n"); LLVM_DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); tooling::Replacements Replaces = reformat(Style, Code, Ranges); auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); return *Result; } static std::string format(llvm::StringRef Code, const FormatStyle &Style) { return format(Code, 0, Code.size(), Style); } static void verifyFormat(llvm::StringRef Code, const FormatStyle &Style) { EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); } static void verifyFormat(llvm::StringRef Code) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); Style.ColumnLimit = 60; // To make writing tests easier. verifyFormat(Code, Style); } }; TEST_F(FormatTestTextProto, KeepsTopLevelEntriesFittingALine) { verifyFormat("field_a: OK field_b: OK field_c: OK field_d: OK field_e: OK"); } TEST_F(FormatTestTextProto, SupportsMessageFields) { verifyFormat("msg_field: {}"); verifyFormat("msg_field: { field_a: A }"); verifyFormat("msg_field: { field_a: \"OK\" field_b: 123 }"); verifyFormat("msg_field: {\n" " field_a: 1\n" " field_b: OK\n" " field_c: \"OK\"\n" " field_d: 123\n" " field_e: 23\n" "}"); verifyFormat("msg_field {}"); verifyFormat("msg_field { field_a: A }"); verifyFormat("msg_field { field_a: \"OK\" field_b: 123 }"); verifyFormat("msg_field {\n" " field_a: 1\n" " field_b: OK\n" " field_c: \"OK\"\n" " field_d: 123\n" " field_e: 23.0\n" " field_f: false\n" " field_g: 'lala'\n" " field_h: 1234.567e-89\n" "}"); verifyFormat("msg_field: { msg_field { field_a: 1 } }"); verifyFormat("id: \"ala.bala\"\n" "item { type: ITEM_A rank: 1 score: 90.0 }\n" "item { type: ITEM_B rank: 2 score: 70.5 }\n" "item {\n" " type: ITEM_A\n" " rank: 3\n" " score: 20.0\n" " description: \"the third item has a description\"\n" "}"); } TEST_F(FormatTestTextProto, AvoidsTopLevelBinPacking) { verifyFormat("field_a: OK\n" "field_b: OK\n" "field_c: OK\n" "field_d: OK\n" "field_e: OK\n" "field_f: OK"); verifyFormat("field_a: OK\n" "field_b: \"OK\"\n" "field_c: \"OK\"\n" "msg_field: { field_d: 123 }\n" "field_e: OK\n" "field_f: OK"); verifyFormat("field_a: OK\n" "field_b: \"OK\"\n" "field_c: \"OK\"\n" "msg_field: { field_d: 123 field_e: OK }"); verifyFormat("a: {\n" " field_a: OK\n" " field_b { field_c: OK }\n" " field_d: OKOKOK\n" " field_e: OK\n" "}"); verifyFormat("field_a: OK,\n" "field_b { field_c: OK },\n" "field_d: OKOKOK,\n" "field_e: OK"); } TEST_F(FormatTestTextProto, AddsNewlinesAfterTrailingComments) { verifyFormat("field_a: OK // Comment\n" "field_b: 1"); verifyFormat("field_a: OK\n" "msg_field: {\n" " field_b: OK // Comment\n" "}"); verifyFormat("field_a: OK\n" "msg_field {\n" " field_b: OK // Comment\n" "}"); } TEST_F(FormatTestTextProto, ImplicitStringLiteralConcatenation) { verifyFormat("field_a: 'aaaaa'\n" " 'bbbbb'"); verifyFormat("field_a: \"aaaaa\"\n" " \"bbbbb\""); FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); Style.AlwaysBreakBeforeMultilineStrings = true; verifyFormat("field_a:\n" " 'aaaaa'\n" " 'bbbbb'", Style); verifyFormat("field_a:\n" " \"aaaaa\"\n" " \"bbbbb\"", Style); } TEST_F(FormatTestTextProto, SupportsAngleBracketMessageFields) { // Single-line tests verifyFormat("msg_field <>"); verifyFormat("msg_field: <>"); verifyFormat("msg_field < field_a: OK >"); verifyFormat("msg_field: < field_a: 123 >"); verifyFormat("msg_field < field_a <> >"); verifyFormat("msg_field < field_a < field_b <> > >"); verifyFormat("msg_field: < field_a < field_b: <> > >"); verifyFormat("msg_field < field_a: OK, field_b: \"OK\" >"); verifyFormat("msg_field: < field_a: OK, field_b: \"OK\" >"); // Multiple lines tests verifyFormat("msg_field <\n" " field_a: OK\n" " field_b: <>,\n" " field_c: OK\n" ">"); verifyFormat("msg_field <\n" " field_a { field_b: 1 },\n" " field_c: < f_d: 2 >\n" ">"); verifyFormat("msg_field: <\n" " field_a: OK\n" " field_b: <>,\n" " field_c: OK\n" ">"); verifyFormat("msg_field: <\n" " field_a { field_b: 1 },\n" " field_c: < fd_d: 2 >\n" ">"); verifyFormat("field_a: \"OK\",\n" "msg_field: < field_b: 123 >,\n" "field_c: {}"); verifyFormat("field_a < field_b: 1 >,\n" "msg_fid: < fiel_b: 123 >,\n" "field_c <>"); verifyFormat("field_a < field_b: 1 >\n" "msg_fied: < field_b: 123 >\n" "field_c <>"); verifyFormat("field <\n" " field < field: <> >,\n" " field <>\n" ">\n" "field: < field: 1 >"); verifyFormat("msg_field <\n" " field_a: OK\n" " field_b: \"OK\"\n" " field_c: 1\n" " field_d: 12.5\n" " field_e: OK\n" ">"); verifyFormat("msg_field: <>\n" "field_c: \"OK\",\n" "msg_field: < field_d: 123 >\n" "field_e: OK\n" "msg_field: < field_d: 12 >"); verifyFormat("field_a: OK,\n" "field_b < field_c: OK >,\n" "field_d: < 12.5 >,\n" "field_e: OK"); verifyFormat("field_a: OK\n" "field_b < field_c: OK >\n" "field_d: < 12.5 >\n" "field_e: OKOKOK"); verifyFormat("msg_field <\n" " field_a: OK,\n" " field_b < field_c: OK >,\n" " field_d: < 12.5 >,\n" " field_e: OK\n" ">"); verifyFormat("msg_field <\n" " field_a: < field: OK >,\n" " field_b < field_c: OK >,\n" " field_d: < 12.5 >,\n" " field_e: OK,\n" ">"); verifyFormat("msg_field: <\n" " field_a: \"OK\"\n" " msg_field: { field_b: OK }\n" " field_g: OK\n" " field_g: OK\n" " field_g: OK\n" ">"); verifyFormat("field_a {\n" " field_d: ok\n" " field_b: < field_c: 1 >\n" " field_d: ok\n" " field_d: ok\n" "}"); verifyFormat("field_a: {\n" " field_d: ok\n" " field_b: < field_c: 1 >\n" " field_d: ok\n" " field_d: ok\n" "}"); verifyFormat("field_a: <\n" " f1: 1,\n" " f2: <>\n" ">\n" "field_b <\n" " field_b1: <>\n" " field_b2: ok,\n" " field_b3: <\n" " field_x {} // Comment\n" " field_y: { field_z: 1 }\n" " field_w: ok\n" " >\n" " field {\n" " field_x <> // Comment\n" " field_y: < field_z: 1 >\n" " field_w: ok\n" " msg_field: <\n" " field: <>\n" " field: < field: 1 >\n" " field: < field: 2 >\n" " field: < field: 3 >\n" " field: < field: 4 >\n" " field: ok\n" " >\n" " }\n" ">\n" "field: OK,\n" "field_c < field < field <> > >"); verifyFormat("app_id: 'com.javax.swing.salsa.latino'\n" "head_id: 1\n" "data < key: value >"); verifyFormat("app_id: 'com.javax.swing.salsa.latino'\n" "head_id: 1\n" "data < key: value >\n" "tail_id: 2"); verifyFormat("app_id: 'com.javax.swing.salsa.latino'\n" "head_id: 1\n" "data < key: value >\n" "data { key: value }"); verifyFormat("app {\n" " app_id: 'com.javax.swing.salsa.latino'\n" " head_id: 1\n" " data < key: value >\n" "}"); verifyFormat("app: {\n" " app_id: 'com.javax.swing.salsa.latino'\n" " head_id: 1\n" " data < key: value >\n" "}"); verifyFormat("app_id: 'com.javax.swing.salsa.latino'\n" "headheadheadheadheadhead_id: 1\n" "product_data { product { 1 } }"); verifyFormat("app_id: 'com.javax.swing.salsa.latino'\n" "headheadheadheadheadhead_id: 1\n" "product_data < product { 1 } >"); verifyFormat("app_id: 'com.javax.swing.salsa.latino'\n" "headheadheadheadheadhead_id: 1\n" "product_data < product < 1 > >"); verifyFormat("app <\n" " app_id: 'com.javax.swing.salsa.latino'\n" " headheadheadheadheadhead_id: 1\n" " product_data < product { 1 } >\n" ">"); verifyFormat("dcccwrnfioeruvginerurneitinfo {\n" " exte3nsionrnfvui { key: value }\n" "}"); } TEST_F(FormatTestTextProto, DiscardsUnbreakableTailIfCanBreakAfter) { // The two closing braces count towards the string UnbreakableTailLength, but // since we have broken after the corresponding opening braces, we don't // consider that length for string breaking. verifyFormat( "foo: {\n" " bar: {\n" " text: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n" " }\n" "}"); } TEST_F(FormatTestTextProto, KeepsLongStringLiteralsOnSameLine) { verifyFormat( "foo: {\n" " text: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaasaaaaaaaaaa\"\n" "}"); } TEST_F(FormatTestTextProto, KeepsCommentsIndentedInList) { verifyFormat("aaaaaaaaaa: 100\n" "bbbbbbbbbbbbbbbbbbbbbbbbbbb: 200\n" "# Single line comment for stuff here.\n" "cccccccccccccccccccccccc: 3849\n" "# Multiline comment for stuff here.\n" "# Multiline comment for stuff here.\n" "# Multiline comment for stuff here.\n" "cccccccccccccccccccccccc: 3849"); } TEST_F(FormatTestTextProto, UnderstandsHashComments) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); Style.ColumnLimit = 60; // To make writing tests easier. EXPECT_EQ("aaa: 100\n" "## this is a double-hash comment.\n" "bb: 100\n" "## another double-hash comment.\n" "### a triple-hash comment\n" "cc: 200\n" "### another triple-hash comment\n" "#### a quadriple-hash comment\n" "dd: 100\n" "#### another quadriple-hash comment\n", format("aaa: 100\n" "##this is a double-hash comment.\n" "bb: 100\n" "## another double-hash comment.\n" "###a triple-hash comment\n" "cc: 200\n" "### another triple-hash comment\n" "####a quadriple-hash comment\n" "dd: 100\n" "#### another quadriple-hash comment\n", Style)); } TEST_F(FormatTestTextProto, FormatsExtensions) { verifyFormat("[type] { key: value }"); verifyFormat("[type] {\n" " keyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: value\n" "}"); verifyFormat("[type.type] { key: value }"); verifyFormat("[type.type] < key: value >"); verifyFormat("[type.type/type.type] { key: value }"); verifyFormat("msg {\n" " [type.type] { key: value }\n" "}"); verifyFormat("msg {\n" " [type.type] {\n" " keyyyyyyyyyyyyyy: valuuuuuuuuuuuuuuuuuuuuuuuuue\n" " }\n" "}"); verifyFormat("key: value\n" "[a.b] { key: value }"); verifyFormat("msg: <\n" " key: value\n" " [a.b.c/d.e]: < key: value >\n" " [f.g]: <\n" " key: valueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n" " key: {}\n" " >\n" " key {}\n" " [h.i.j] < key: value >\n" " [a]: {\n" " [b.c]: {}\n" " [d] <>\n" " [e/f]: 1\n" " }\n" ">"); verifyFormat("[longg.long.long.long.long.long.long.long.long.long.long\n" " .longg.longlong] { key: value }"); verifyFormat("[longg.long.long.long.long.long.long.long.long.long.long\n" " .longg.longlong] {\n" " key: value\n" " key: value\n" " key: value\n" " key: value\n" "}"); verifyFormat("[longg.long.long.long.long.long.long.long.long.long\n" " .long/longg.longlong] { key: value }"); verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/\n" " bbbbbbbbbbbbbb] { key: value }"); // These go over the column limit intentionally, since the alternative // [aa..a\n] is worse. verifyFormat( "[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] {\n" " key: value\n" "}"); verifyFormat( "[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] {\n" " [type.type] {\n" " keyyyyyyyyyyyyyy: valuuuuuuuuuuuuuuuuuuuuuuuuue\n" " }\n" "}"); verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/\n" " bbbbbbb] {\n" " [type.type] {\n" " keyyyyyyyyyyyyyy: valuuuuuuuuuuuuuuuuuuuuuuuuue\n" " }\n" "}"); verifyFormat( "aaaaaaaaaaaaaaa {\n" " bbbbbb {\n" " [a.b/cy] {\n" " eeeeeeeeeeeee: \"The lazy coo cat jumps over the lazy hot dog\"\n" " }\n" " }\n" "}"); } TEST_F(FormatTestTextProto, SpacesAroundPercents) { verifyFormat("key: %d"); verifyFormat("key: 0x%04x"); verifyFormat("key: \"%d %d\""); } TEST_F(FormatTestTextProto, FormatsRepeatedListInitializers) { verifyFormat("keys: []"); verifyFormat("keys: [ 1 ]"); verifyFormat("keys: [ 'ala', 'bala' ]"); verifyFormat("keys: [\n" " 'ala',\n" " 'bala',\n" " 'porto',\n" " 'kala',\n" " 'too',\n" " 'long',\n" " 'ng'\n" "]"); verifyFormat("key: item\n" "keys: [\n" " 'ala',\n" " 'bala',\n" " 'porto',\n" " 'kala',\n" " 'too',\n" " 'long',\n" " 'long',\n" " 'long'\n" "]\n" "key: item\n" "msg {\n" " key: item\n" " keys: [\n" " 'ala',\n" " 'bala',\n" " 'porto',\n" " 'kala',\n" " 'too',\n" " 'long',\n" " 'long'\n" " ]\n" "}\n" "key: value"); FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); Style.ColumnLimit = 60; // To make writing tests easier. Style.Cpp11BracedListStyle = true; verifyFormat("keys: [1]", Style); } TEST_F(FormatTestTextProto, AcceptsOperatorAsKey) { verifyFormat("aaaaaaaaaaa: <\n" " bbbbbbbbb: <\n" " ccccccccccccccccccccccc: <\n" " operator: 1\n" " operator: 2\n" " operator: 3\n" " operator { key: value }\n" " >\n" " >\n" ">"); } TEST_F(FormatTestTextProto, BreaksConsecutiveStringLiterals) { verifyFormat("ala: \"str1\"\n" " \"str2\"\n"); } TEST_F(FormatTestTextProto, PutsMultipleEntriesInExtensionsOnNewlines) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); verifyFormat("pppppppppp: {\n" " ssssss: \"http://example.com/blahblahblah\"\n" " ppppppp: \"sssss/MMMMMMMMMMMM\"\n" " [ns.sssss.eeeeeeeee.eeeeeeeeeeeeeee] { begin: 24 end: 252 }\n" " [ns.sssss.eeeeeeeee.eeeeeeeeeeeeeee] {\n" " begin: 24\n" " end: 252\n" " key: value\n" " key: value\n" " }\n" "}", Style); } TEST_F(FormatTestTextProto, BreaksAfterBraceFollowedByClosingBraceOnNextLine) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); Style.ColumnLimit = 60; verifyFormat("keys: [\n" " data: { item: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }\n" "]"); verifyFormat("keys: <\n" " data: { item: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }\n" ">"); } TEST_F(FormatTestTextProto, BreaksEntriesOfSubmessagesContainingSubmessages) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); Style.ColumnLimit = 60; // The column limit allows for the keys submessage to be put on 1 line, but we // break it since it contains a submessage an another entry. verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: 'aaaaaaaaaaaaaaaa'\n" " sub <>\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: 'aaaaaaaaaaaaaaaa'\n" " sub {}\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub {}\n" " sub: <>\n" " sub: []\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: 'aaaaaaaaaaa'\n" " sub { msg: 1 }\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: 'aaaaaaaaaaa'\n" " sub: { msg: 1 }\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: 'aaaaaaaaaaa'\n" " sub < msg: 1 >\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: 'aaaaaaaaaaa'\n" " sub: [ msg: 1 ]\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: <\n" " item: 'aaaaaaaaaaa'\n" " sub: [ 1, 2 ]\n" ">"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub {}\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub: []\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub <>\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub { key: value }\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub: [ 1, 2 ]\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " sub < sub_2: {} >\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: data\n" " sub: [ 1, 2 ]\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("key: valueeeeeeee\n" "keys: {\n" " item: data\n" " sub < sub_2: {} >\n" " item: 'aaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("sub: {\n" " key: valueeeeeeee\n" " keys: {\n" " sub: [ 1, 2 ]\n" " item: 'aaaaaaaaaaaaaaaa'\n" " }\n" "}"); verifyFormat("sub: {\n" " key: 1\n" " sub: {}\n" "}\n" "# comment\n"); verifyFormat("sub: {\n" " key: 1\n" " # comment\n" " sub: {}\n" "}"); } TEST_F(FormatTestTextProto, PreventBreaksBetweenKeyAndSubmessages) { verifyFormat("submessage: {\n" " key: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("submessage {\n" " key: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'\n" "}"); verifyFormat("submessage: <\n" " key: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'\n" ">"); verifyFormat("submessage <\n" " key: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'\n" ">"); verifyFormat("repeatedd: [\n" " 'eyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'\n" "]"); // "{" is going over the column limit. verifyFormat( "submessageeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee: {\n" " key: 'aaaaa'\n" "}"); } TEST_F(FormatTestTextProto, FormatsCommentsAtEndOfFile) { verifyFormat("key: value\n" "# endfile comment"); verifyFormat("key: value\n" "// endfile comment"); verifyFormat("key: value\n" "// endfile comment 1\n" "// endfile comment 2"); verifyFormat("submessage { key: value }\n" "# endfile comment"); verifyFormat("submessage <\n" " key: value\n" " item {}\n" ">\n" "# endfile comment"); } TEST_F(FormatTestTextProto, KeepsAmpersandsNextToKeys) { verifyFormat("@tmpl { field: 1 }"); verifyFormat("@placeholder: 1"); verifyFormat("@name <>"); verifyFormat("submessage: @base { key: value }"); verifyFormat("submessage: @base {\n" " key: value\n" " item: {}\n" "}"); verifyFormat("submessage: {\n" " msg: @base {\n" " yolo: {}\n" " key: value\n" " }\n" " key: value\n" "}"); } } // namespace format } // end namespace clang