gitlab.com/greut/eclint@v0.5.2-0.20240402114752-14681fe6e0bf/fix_test.go (about) 1 package eclint 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "testing" 8 9 "github.com/editorconfig/editorconfig-core-go/v2" 10 "github.com/google/go-cmp/cmp" 11 ) 12 13 func TestFixEndOfLine(t *testing.T) { //nolint:gocognit 14 tests := []struct { 15 Name string 16 Lines [][]byte 17 }{ 18 { 19 Name: "a file with many lines", 20 Lines: [][]byte{ 21 []byte("A file"), 22 []byte("With many lines"), 23 }, 24 }, 25 { 26 Name: "a file with many lines and a final newline", 27 Lines: [][]byte{ 28 []byte("A file"), 29 []byte("With many lines"), 30 []byte("and a final newline."), 31 []byte(""), 32 }, 33 }, 34 } 35 36 ctx := context.TODO() 37 38 for _, tc := range tests { 39 tc := tc 40 41 file := bytes.Join(tc.Lines, []byte("\n")) 42 fileSize := int64(len(file)) 43 44 // Test the nominal case 45 t.Run(tc.Name, func(t *testing.T) { 46 t.Parallel() 47 48 def, err := newDefinition(&editorconfig.Definition{ 49 EndOfLine: editorconfig.EndOfLineLf, 50 }) 51 if err != nil { 52 t.Fatal(err) 53 } 54 55 r := bytes.NewReader(file) 56 57 out, fixed, err := fix(ctx, r, fileSize, "utf-8", def) 58 if err != nil { 59 t.Fatalf("no errors where expected, got %s", err) 60 } 61 62 if fixed { 63 t.Errorf("file should not have been fixed") 64 } 65 66 result, err := io.ReadAll(out) 67 if err != nil { 68 t.Fatalf("cannot read result %s", err) 69 } 70 71 if !cmp.Equal(file, result) { 72 t.Errorf("diff %s", cmp.Diff(file, result)) 73 } 74 }) 75 76 // Test the inverse 77 t.Run(tc.Name, func(t *testing.T) { 78 t.Parallel() 79 80 def, err := newDefinition(&editorconfig.Definition{ 81 EndOfLine: editorconfig.EndOfLineCrLf, 82 }) 83 if err != nil { 84 t.Fatal(err) 85 } 86 87 r := bytes.NewReader(file) 88 89 out, fixed, err := fix(ctx, r, fileSize, "utf-8", def) 90 if err != nil { 91 t.Fatalf("no errors where expected, got %s", err) 92 } 93 94 if !fixed { 95 t.Errorf("file should have been fixed") 96 } 97 98 result, err := io.ReadAll(out) 99 if err != nil { 100 t.Fatalf("cannot read result %s", err) 101 } 102 103 if cmp.Equal(file, result) { 104 t.Errorf("no differences, the file was not fixed") 105 } 106 }) 107 } 108 } 109 110 func TestFixIndentStyle(t *testing.T) { 111 tests := []struct { 112 Name string 113 IndentSize string 114 IndentStyle string 115 File []byte 116 }{ 117 { 118 Name: "space to tab", 119 IndentStyle: "tab", 120 IndentSize: "2", 121 File: []byte("\t\t \tA line\n"), 122 }, 123 { 124 Name: "tab to space", 125 IndentStyle: "space", 126 IndentSize: "2", 127 File: []byte("\t\t \tA line\n"), 128 }, 129 } 130 131 ctx := context.TODO() 132 133 for _, tc := range tests { 134 tc := tc 135 136 fileSize := int64(len(tc.File)) 137 138 t.Run(tc.Name, func(t *testing.T) { 139 t.Parallel() 140 141 def, err := newDefinition(&editorconfig.Definition{ 142 EndOfLine: "lf", 143 IndentStyle: tc.IndentStyle, 144 IndentSize: tc.IndentSize, 145 }) 146 if err != nil { 147 t.Fatal(err) 148 } 149 150 if err := indentStyle(tc.IndentStyle, def.IndentSize, tc.File); err == nil { 151 t.Errorf("the initial file should fail") 152 } 153 154 r := bytes.NewReader(tc.File) 155 156 out, _, err := fix(ctx, r, fileSize, "utf-8", def) 157 if err != nil { 158 t.Fatalf("no errors where expected, got %s", err) 159 } 160 161 result, err := io.ReadAll(out) 162 if err != nil { 163 t.Fatalf("cannot read result %s", err) 164 } 165 166 if cmp.Equal(tc.File, result) { 167 t.Errorf("no changes!?") 168 } 169 170 if err := indentStyle(tc.IndentStyle, def.IndentSize, result); err != nil { 171 t.Errorf("no errors were expected, got %s", err) 172 } 173 }) 174 } 175 } 176 177 func TestFixTrimTrailingWhitespace(t *testing.T) { 178 tests := []struct { 179 Name string 180 Lines [][]byte 181 }{ 182 { 183 Name: "space", 184 Lines: [][]byte{ 185 []byte("A file"), 186 []byte(" with spaces "), 187 []byte(" at the end "), 188 []byte(" "), 189 }, 190 }, 191 { 192 Name: "tabs", 193 Lines: [][]byte{ 194 []byte("A file"), 195 []byte(" with tabs\t"), 196 []byte(" at the end\t\t"), 197 []byte("\t"), 198 }, 199 }, 200 { 201 Name: "tabs and spaces", 202 Lines: [][]byte{ 203 []byte("A file"), 204 []byte(" with tabs\t\t "), 205 []byte(" and spaces\t \t"), 206 []byte(" at the end \t"), 207 }, 208 }, 209 } 210 211 for _, tc := range tests { 212 tc := tc 213 214 t.Run(tc.Name, func(t *testing.T) { 215 t.Parallel() 216 217 for _, l := range tc.Lines { 218 m, _ := fixTrailingWhitespace(l) 219 220 err := checkTrimTrailingWhitespace(m) 221 if err != nil { 222 t.Errorf("no errors were expected. %s", err) 223 } 224 } 225 }) 226 } 227 } 228 229 func TestFixInsertFinalNewline(t *testing.T) { 230 eolVariants := [][]byte{ 231 {cr}, 232 {lf}, 233 {cr, lf}, 234 } 235 236 insertFinalNewlineVariants := []bool{true, false} 237 newlinesAtEOLVariants := []int{0, 1, 3} 238 239 type Test struct { 240 InsertFinalNewline bool 241 File []byte 242 EolVariant []byte 243 NewlinesAtEOL int 244 } 245 246 tests := make([]Test, 0, 54) 247 248 // single line tests 249 singleLineFile := []byte(`A single line file.`) 250 251 for _, eolVariant := range eolVariants { 252 for _, insertFinalNewlineVariant := range insertFinalNewlineVariants { 253 for newlinesAtEOL := range newlinesAtEOLVariants { 254 file := singleLineFile 255 for i := 0; i < newlinesAtEOL; i++ { 256 file = append(file, eolVariant...) 257 } 258 259 tests = append(tests, 260 Test{ 261 InsertFinalNewline: insertFinalNewlineVariant, 262 File: file, 263 EolVariant: eolVariant, 264 NewlinesAtEOL: newlinesAtEOL, 265 }, 266 ) 267 } 268 } 269 } 270 271 // multiline tests 272 multilineComponents := [][]byte{[]byte(`A`), []byte(`multiline`), []byte(`file.`)} 273 274 for _, eolVariant := range eolVariants { 275 multilineFile := bytes.Join(multilineComponents, eolVariant) 276 277 for _, insertFinalNewlineVariant := range insertFinalNewlineVariants { 278 for newlinesAtEOL := range newlinesAtEOLVariants { 279 file := multilineFile 280 for i := 0; i < newlinesAtEOL; i++ { 281 file = append(file, eolVariant...) 282 } 283 284 tests = append(tests, 285 Test{ 286 InsertFinalNewline: insertFinalNewlineVariant, 287 File: file, 288 EolVariant: eolVariant, 289 NewlinesAtEOL: newlinesAtEOL, 290 }, 291 ) 292 } 293 } 294 } 295 296 // empty file tests 297 emptyFile := []byte("") 298 299 for _, eolVariant := range eolVariants { 300 for _, insertFinalNewlineVariant := range insertFinalNewlineVariants { 301 tests = append(tests, 302 Test{ 303 InsertFinalNewline: insertFinalNewlineVariant, 304 File: emptyFile, 305 EolVariant: eolVariant, 306 }, 307 ) 308 } 309 } 310 311 for _, tc := range tests { 312 tc := tc 313 314 t.Run("TestFixInsertFinalNewline", func(t *testing.T) { 315 t.Parallel() 316 317 buf := bytes.Buffer{} 318 buf.Write(tc.File) 319 before := buf.Bytes() 320 fixInsertFinalNewline(&buf, tc.InsertFinalNewline, tc.EolVariant) 321 after := buf.Bytes() 322 323 err := checkInsertFinalNewline(buf.Bytes(), tc.InsertFinalNewline) 324 if err != nil { 325 t.Logf("before: %q", string(before)) 326 t.Logf("after: %q", string(after)) 327 t.Errorf("encountered error %s with test configuration %+v", err, tc) 328 } 329 }) 330 } 331 }