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  }