1 //===- DefinitionBlockSeparatorTest.cpp - Formatting unit 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 "FormatTestUtils.h" 10 #include "clang/Format/Format.h" 11 12 #include "llvm/Support/Debug.h" 13 #include "gtest/gtest.h" 14 15 #define DEBUG_TYPE "definition-block-separator-test" 16 17 namespace clang { 18 namespace format { 19 namespace { 20 21 class DefinitionBlockSeparatorTest : public ::testing::Test { 22 protected: 23 static std::string 24 separateDefinitionBlocks(llvm::StringRef Code, 25 const std::vector<tooling::Range> &Ranges, 26 const FormatStyle &Style = getLLVMStyle()) { 27 LLVM_DEBUG(llvm::errs() << "---\n"); 28 LLVM_DEBUG(llvm::errs() << Code << "\n\n"); 29 tooling::Replacements Replaces = reformat(Style, Code, Ranges, "<stdin>"); 30 auto Result = applyAllReplacements(Code, Replaces); 31 EXPECT_TRUE(static_cast<bool>(Result)); 32 LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); 33 return *Result; 34 } 35 36 static std::string 37 separateDefinitionBlocks(llvm::StringRef Code, 38 const FormatStyle &Style = getLLVMStyle()) { 39 return separateDefinitionBlocks( 40 Code, 41 /*Ranges=*/{1, tooling::Range(0, Code.size())}, Style); 42 } 43 44 static void verifyFormat(llvm::StringRef Code, 45 const FormatStyle &Style = getLLVMStyle(), 46 llvm::StringRef ExpectedCode = "") { 47 bool HasOriginalCode = true; 48 if (ExpectedCode == "") { 49 ExpectedCode = Code; 50 HasOriginalCode = false; 51 } 52 53 FormatStyle InverseStyle = Style; 54 if (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always) 55 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 56 else 57 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 58 EXPECT_EQ(ExpectedCode.str(), separateDefinitionBlocks(ExpectedCode, Style)) 59 << "Expected code is not stable"; 60 std::string InverseResult = separateDefinitionBlocks(Code, InverseStyle); 61 EXPECT_NE(Code.str(), InverseResult) 62 << "Inverse formatting makes no difference"; 63 std::string CodeToFormat = 64 HasOriginalCode ? Code.str() : removeEmptyLines(Code); 65 std::string Result = separateDefinitionBlocks(CodeToFormat, Style); 66 EXPECT_EQ(ExpectedCode.str(), Result) << "Test failed. Formatted:\n" 67 << Result; 68 } 69 70 static std::string removeEmptyLines(llvm::StringRef Code) { 71 std::string Result = ""; 72 for (auto Char : Code.str()) { 73 if (Result.size()) { 74 auto LastChar = Result.back(); 75 if ((Char == '\n' && LastChar == '\n') || 76 (Char == '\r' && (LastChar == '\r' || LastChar == '\n'))) 77 continue; 78 } 79 Result.push_back(Char); 80 } 81 return Result; 82 } 83 }; 84 85 TEST_F(DefinitionBlockSeparatorTest, Basic) { 86 FormatStyle Style = getLLVMStyle(); 87 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 88 verifyFormat("int foo(int i, int j) {\n" 89 " int r = i + j;\n" 90 " return r;\n" 91 "}\n" 92 "\n" 93 "int bar(int j, int k) {\n" 94 " int r = j + k;\n" 95 " return r;\n" 96 "}", 97 Style); 98 99 verifyFormat("struct foo {\n" 100 " int i, j;\n" 101 "};\n" 102 "\n" 103 "struct bar {\n" 104 " int j, k;\n" 105 "};", 106 Style); 107 108 verifyFormat("class foo {\n" 109 " int i, j;\n" 110 "};\n" 111 "\n" 112 "class bar {\n" 113 " int j, k;\n" 114 "};", 115 Style); 116 117 verifyFormat("namespace foo {\n" 118 "int i, j;\n" 119 "}\n" 120 "\n" 121 "namespace bar {\n" 122 "int j, k;\n" 123 "}", 124 Style); 125 126 verifyFormat("enum Foo { FOO, BAR };\n" 127 "\n" 128 "enum Bar { FOOBAR, BARFOO };\n", 129 Style); 130 } 131 132 TEST_F(DefinitionBlockSeparatorTest, Always) { 133 FormatStyle Style = getLLVMStyle(); 134 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 135 std::string Prefix = "namespace {\n"; 136 std::string Postfix = "enum Foo { FOO, BAR };\n" 137 "\n" 138 "int foo(int i, int j) {\n" 139 " int r = i + j;\n" 140 " return r;\n" 141 "}\n" 142 "\n" 143 "int i, j, k;\n" 144 "\n" 145 "int bar(int j, int k) {\n" 146 " int r = j * k;\n" 147 " return r;\n" 148 "}\n" 149 "\n" 150 "enum Bar { FOOBAR, BARFOO };\n" 151 "} // namespace"; 152 verifyFormat(Prefix + "\n\n\n" + removeEmptyLines(Postfix), Style, 153 Prefix + Postfix); 154 } 155 156 TEST_F(DefinitionBlockSeparatorTest, Never) { 157 FormatStyle Style = getLLVMStyle(); 158 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 159 std::string Prefix = "namespace {\n"; 160 std::string Postfix = "enum Foo { FOO, BAR };\n" 161 "\n" 162 "int foo(int i, int j) {\n" 163 " int r = i + j;\n" 164 " return r;\n" 165 "}\n" 166 "\n" 167 "int i, j, k;\n" 168 "\n" 169 "int bar(int j, int k) {\n" 170 " int r = j * k;\n" 171 " return r;\n" 172 "}\n" 173 "\n" 174 "enum Bar { FOOBAR, BARFOO };\n" 175 "} // namespace"; 176 verifyFormat(Prefix + "\n\n\n" + Postfix, Style, 177 Prefix + removeEmptyLines(Postfix)); 178 } 179 180 TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) { 181 FormatStyle Style = getLLVMStyle(); 182 Style.BreakBeforeBraces = FormatStyle::BS_Allman; 183 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 184 verifyFormat("enum Foo\n" 185 "{\n" 186 " FOO,\n" 187 " BAR\n" 188 "};\n" 189 "\n" 190 "int foo(int i, int j)\n" 191 "{\n" 192 " int r = i + j;\n" 193 " return r;\n" 194 "}\n" 195 "\n" 196 "int i, j, k;\n" 197 "\n" 198 "int bar(int j, int k)\n" 199 "{\n" 200 " int r = j * k;\n" 201 " return r;\n" 202 "}\n" 203 "\n" 204 "enum Bar\n" 205 "{\n" 206 " FOOBAR,\n" 207 " BARFOO\n" 208 "};", 209 Style); 210 } 211 212 TEST_F(DefinitionBlockSeparatorTest, Leave) { 213 FormatStyle Style = getLLVMStyle(); 214 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave; 215 Style.MaxEmptyLinesToKeep = 3; 216 std::string LeaveAs = "namespace {\n" 217 "\n" 218 "enum Foo { FOO, BAR };\n" 219 "\n\n\n" 220 "int foo(int i, int j) {\n" 221 " int r = i + j;\n" 222 " return r;\n" 223 "}\n" 224 "\n" 225 "int i, j, k;\n" 226 "\n" 227 "int bar(int j, int k) {\n" 228 " int r = j * k;\n" 229 " return r;\n" 230 "}\n" 231 "\n" 232 "enum Bar { FOOBAR, BARFOO };\n" 233 "} // namespace"; 234 verifyFormat(LeaveAs, Style, LeaveAs); 235 } 236 237 TEST_F(DefinitionBlockSeparatorTest, CSharp) { 238 FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp); 239 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 240 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 241 Style.AllowShortEnumsOnASingleLine = false; 242 verifyFormat("namespace {\r\n" 243 "public class SomeTinyClass {\r\n" 244 " int X;\r\n" 245 "}\r\n" 246 "\r\n" 247 "public class AnotherTinyClass {\r\n" 248 " int Y;\r\n" 249 "}\r\n" 250 "\r\n" 251 "internal static String toString() {\r\n" 252 "}\r\n" 253 "\r\n" 254 "public enum var {\r\n" 255 " none,\r\n" 256 " @string,\r\n" 257 " bool,\r\n" 258 " @enum\r\n" 259 "}\r\n" 260 "\r\n" 261 "[STAThread]\r\n" 262 "static void Main(string[] args) {\r\n" 263 " Console.WriteLine(\"HelloWorld\");\r\n" 264 "}\r\n" 265 "\r\n" 266 "static decimal Test() {\r\n" 267 "}\r\n" 268 "}\r\n" 269 "\r\n" 270 "public class FoobarClass {\r\n" 271 " int foobar;\r\n" 272 "}", 273 Style); 274 } 275 276 TEST_F(DefinitionBlockSeparatorTest, JavaScript) { 277 FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript); 278 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 279 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 280 Style.AllowShortEnumsOnASingleLine = false; 281 verifyFormat("export const enum Foo {\n" 282 " A = 1,\n" 283 " B\n" 284 "}\n" 285 "\n" 286 "export function A() {\n" 287 "}\n" 288 "\n" 289 "export default function B() {\n" 290 "}\n" 291 "\n" 292 "export function C() {\n" 293 "}\n" 294 "\n" 295 "var t, p, q;\n" 296 "\n" 297 "export abstract class X {\n" 298 " y: number;\n" 299 "}\n" 300 "\n" 301 "export const enum Bar {\n" 302 " D = 1,\n" 303 " E\n" 304 "}", 305 Style); 306 } 307 } // namespace 308 } // namespace format 309 } // namespace clang 310