...

Source file src/github.com/redhat-developer/odo/pkg/util/file_indexer_test.go

Documentation: github.com/redhat-developer/odo/pkg/util

     1  package util
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/google/go-cmp/cmp/cmpopts"
    11  
    12  	"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
    13  )
    14  
    15  func TestCheckGitIgnoreFile(t *testing.T) {
    16  
    17  	// create a fake fs in memory
    18  	fs := filesystem.NewFakeFs()
    19  	// create a context directory on fake fs
    20  	contextDir, err := fs.TempDir(os.TempDir(), "context")
    21  	if err != nil {
    22  		t.Error(err)
    23  	}
    24  
    25  	gitignorePath := filepath.Join(contextDir, DotGitIgnoreFile)
    26  
    27  	tests := []struct {
    28  		testName        string
    29  		create          bool
    30  		gitIgnoreCreate func(create bool, contextDir string, fs filesystem.Filesystem) error
    31  		directory       string
    32  		want            string
    33  		wantErr         bool
    34  	}{
    35  		{
    36  			testName:        "Test when .gitignore does not exist",
    37  			create:          true,
    38  			gitIgnoreCreate: mockDirectoryInfo,
    39  			directory:       contextDir,
    40  			want:            gitignorePath,
    41  			wantErr:         false,
    42  		},
    43  		{
    44  			testName:        "Test when .gitignore exists",
    45  			create:          false,
    46  			gitIgnoreCreate: mockDirectoryInfo,
    47  			directory:       contextDir,
    48  			want:            gitignorePath,
    49  			wantErr:         false,
    50  		},
    51  	}
    52  
    53  	for _, tt := range tests {
    54  
    55  		err := tt.gitIgnoreCreate(tt.create, tt.directory, fs)
    56  		if err != nil {
    57  			t.Error(err)
    58  		}
    59  
    60  		t.Run(tt.testName, func(t *testing.T) {
    61  
    62  			gitIgnoreFilePath, isNew, err := touchGitIgnoreFile(tt.directory, fs)
    63  
    64  			if tt.want != gitIgnoreFilePath {
    65  				t.Errorf("touchGitIgnoreFile unexpected error %v, while creating .gitignore file", err)
    66  			}
    67  
    68  			if !tt.wantErr == (err != nil) {
    69  				t.Errorf("touchGitIgnoreFile unexpected error %v, wantErr %v", err, tt.wantErr)
    70  			}
    71  
    72  			if tt.create != isNew {
    73  				t.Errorf("touchGitIgnoreFile: expected tt.create=%v, got %v", tt.create, isNew)
    74  			}
    75  		})
    76  	}
    77  
    78  }
    79  
    80  func TestAddOdoDirectory(t *testing.T) {
    81  
    82  	// create a fake fs in memory
    83  	fs := filesystem.NewFakeFs()
    84  	// create a context directory on fake fs
    85  	contextDir, err := fs.TempDir(os.TempDir(), "context")
    86  	if err != nil {
    87  		t.Error(err)
    88  	}
    89  
    90  	gitignorePath := filepath.Join(contextDir, DotGitIgnoreFile)
    91  
    92  	tests := []struct {
    93  		testName        string
    94  		create          bool
    95  		gitIgnoreCreate func(create bool, contextDir string, fs filesystem.Filesystem) error
    96  		directory       string
    97  		wantErr         bool
    98  	}{
    99  		{
   100  			testName:        "Test when .odo added to .gitignore",
   101  			create:          false,
   102  			gitIgnoreCreate: mockDirectoryInfo,
   103  			directory:       gitignorePath,
   104  			wantErr:         false,
   105  		},
   106  	}
   107  
   108  	for _, tt := range tests {
   109  
   110  		err := tt.gitIgnoreCreate(tt.create, tt.directory, fs)
   111  		if err != nil {
   112  			t.Error(err)
   113  		}
   114  
   115  		t.Run(tt.testName, func(t *testing.T) {
   116  
   117  			err := addOdoDirectory(tt.directory, fs)
   118  
   119  			if !tt.wantErr == (err != nil) {
   120  				t.Errorf("addOdoFileIndex unexpected error %v, wantErr %v", err, tt.wantErr)
   121  			}
   122  
   123  		})
   124  	}
   125  }
   126  
   127  func mockDirectoryInfo(create bool, contextDir string, fs filesystem.Filesystem) error {
   128  
   129  	if !create {
   130  		err := fs.MkdirAll(filepath.Join(contextDir, DotGitIgnoreFile), os.ModePerm)
   131  		if err != nil {
   132  			return err
   133  		}
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  func TestCalculateFileDataKeyFromPath(t *testing.T) {
   140  
   141  	// create a temp dir for the fake component
   142  	directory := t.TempDir()
   143  
   144  	tests := []struct {
   145  		absolutePath   string
   146  		rootDirectory  string
   147  		expectedResult string
   148  	}{
   149  		{
   150  			absolutePath:   filepath.Join(directory, "/path/file1"),
   151  			rootDirectory:  filepath.Join(directory, "/path"),
   152  			expectedResult: "file1",
   153  		},
   154  		{
   155  			absolutePath:   filepath.Join(directory, "/path/path2/file1"),
   156  			rootDirectory:  filepath.Join(directory, "/path/"),
   157  			expectedResult: "path2/file1",
   158  		},
   159  		{
   160  			absolutePath:   filepath.Join(directory, "/path"),
   161  			rootDirectory:  filepath.Join(directory, "/"),
   162  			expectedResult: "path",
   163  		},
   164  	}
   165  
   166  	for _, tt := range tests {
   167  
   168  		t.Run("Expect result: "+tt.expectedResult, func(t *testing.T) {
   169  
   170  			result, err := CalculateFileDataKeyFromPath(tt.absolutePath, tt.rootDirectory)
   171  			if err != nil {
   172  				t.Fatalf("unexpecter error occurred %v", err)
   173  			}
   174  
   175  			if result != filepath.FromSlash(tt.expectedResult) {
   176  				t.Fatalf("unexpected result: %v %v", tt.expectedResult, result)
   177  			}
   178  		})
   179  	}
   180  }
   181  
   182  func TestGenerateNewFileDataEntry(t *testing.T) {
   183  
   184  	// create a temp dir for the fake component
   185  	directory := t.TempDir()
   186  
   187  	tests := []struct {
   188  		testName      string
   189  		absolutePath  string
   190  		rootDirectory string
   191  		expectedKey   string
   192  	}{
   193  		{
   194  			absolutePath:  filepath.Join(directory, "/path1/file1"),
   195  			rootDirectory: filepath.Join(directory, "/path1"),
   196  			expectedKey:   "file1",
   197  		},
   198  		{
   199  			absolutePath:  filepath.Join(directory, "/path2/path2/file1"),
   200  			rootDirectory: filepath.Join(directory, "/path2"),
   201  			expectedKey:   "path2/file1",
   202  		},
   203  		{
   204  			absolutePath:  filepath.Join(directory, "/path3"),
   205  			rootDirectory: filepath.Join(directory, "/"),
   206  			expectedKey:   "path3",
   207  		},
   208  	}
   209  
   210  	for _, tt := range tests {
   211  
   212  		t.Run("Expected key '"+tt.expectedKey+"'", func(t *testing.T) {
   213  
   214  			if err := os.MkdirAll(filepath.Dir(tt.absolutePath), 0750); err != nil {
   215  				t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to create directories for %s: %v", tt.absolutePath, err)
   216  			}
   217  
   218  			if err := os.WriteFile(tt.absolutePath, []byte("non-empty-string"), 0644); err != nil {
   219  				t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to write to index file path: %v", err)
   220  			}
   221  
   222  			key, filedata, err := GenerateNewFileDataEntry(tt.absolutePath, tt.rootDirectory)
   223  
   224  			if err != nil {
   225  				t.Fatalf("Unexpected error occurred %v", err)
   226  			}
   227  
   228  			// Keys are platform specific, so swap to forward slash for Windows before comparison
   229  			key = strings.ReplaceAll(key, "\\", "/")
   230  
   231  			if key != tt.expectedKey {
   232  				t.Fatalf("Key %s did not match expected key %s", key, tt.expectedKey)
   233  			}
   234  
   235  			if filedata == nil {
   236  				t.Fatalf("Filedata should not be null")
   237  			}
   238  
   239  			if filedata.Size == 0 || filedata.LastModifiedDate.IsZero() {
   240  				t.Fatalf("Invalid filedata values %v %v", filedata.Size, filedata.LastModifiedDate)
   241  			}
   242  
   243  		})
   244  	}
   245  }
   246  
   247  func createAndStat(fileName, tempDirectoryName string, fs filesystem.Filesystem) (filesystem.File, os.FileInfo, error) {
   248  	file, err := fs.Create(filepath.Join(tempDirectoryName, fileName))
   249  	if err != nil {
   250  		return nil, nil, err
   251  	}
   252  	stat, err := fs.Stat(file.Name())
   253  	if err != nil {
   254  		return nil, nil, err
   255  	}
   256  	return file, stat, nil
   257  }
   258  
   259  func Test_recursiveChecker(t *testing.T) {
   260  	fs := filesystem.DefaultFs{}
   261  
   262  	tempDirectoryName, err := fs.TempDir(os.TempDir(), "dir0")
   263  	if err != nil {
   264  		t.Errorf("unexpected error: %v", err)
   265  	}
   266  
   267  	jsFileName := "red.js"
   268  	jsFile, jsFileStat, err := createAndStat(jsFileName, tempDirectoryName, fs)
   269  	if err != nil {
   270  		t.Errorf("unexpected error: %v", err)
   271  	}
   272  	jsFileAbsPath := jsFile.Name()
   273  
   274  	readmeFileName := "README.txt"
   275  	readmeFile, readmeFileStat, err := createAndStat(readmeFileName, tempDirectoryName, fs)
   276  	if err != nil {
   277  		t.Errorf("unexpected error: %v", err)
   278  	}
   279  	readmeFileAbsPath := readmeFile.Name()
   280  
   281  	viewsFolderName := "views"
   282  	viewsFolderPath := filepath.Join(tempDirectoryName, viewsFolderName)
   283  	err = fs.MkdirAll(viewsFolderPath, 0755)
   284  	if err != nil {
   285  		t.Errorf("unexpected error: %v", err)
   286  	}
   287  
   288  	htmlRelFilePath := filepath.Join(viewsFolderName, "view.html")
   289  	htmlFile, htmlFileStat, err := createAndStat(filepath.Join("views", "view.html"), tempDirectoryName, fs)
   290  	if err != nil {
   291  		t.Errorf("unexpected error: %v", err)
   292  	}
   293  	htmlFileAbsPath := htmlFile.Name()
   294  
   295  	targetFolderName := "target"
   296  	targetFolderRelPath := filepath.Join(viewsFolderName, targetFolderName)
   297  	targetFolderPath := filepath.Join(tempDirectoryName, targetFolderRelPath)
   298  	err = fs.MkdirAll(targetFolderPath, 0755)
   299  	if err != nil {
   300  		t.Errorf("unexpected error: %v", err)
   301  	}
   302  
   303  	targetFileName := "someFile.txt"
   304  	targetFileRelPath := filepath.Join(viewsFolderName, targetFolderName, targetFileName)
   305  	targetFilePath := filepath.Join(tempDirectoryName, targetFileRelPath)
   306  	_, targetFileStat, err := createAndStat(targetFileRelPath, tempDirectoryName, fs)
   307  	if err != nil {
   308  		t.Errorf("unexpected error: %v", err)
   309  	}
   310  
   311  	targetFolderStat, err := fs.Stat(filepath.Join(tempDirectoryName, viewsFolderName, targetFolderName))
   312  	if err != nil {
   313  		t.Errorf("unexpected error: %v", err)
   314  	}
   315  
   316  	viewsFolderStat, err := fs.Stat(filepath.Join(tempDirectoryName, viewsFolderName))
   317  	if err != nil {
   318  		t.Errorf("unexpected error: %v", err)
   319  	}
   320  
   321  	specialCharFolderName := "[devfile-registry]"
   322  	specialCharFolderPath := filepath.Join(tempDirectoryName, specialCharFolderName)
   323  	err = fs.MkdirAll(specialCharFolderPath, 0755)
   324  	if err != nil {
   325  		t.Errorf("unexpected error: %v", err)
   326  	}
   327  
   328  	fileInsideSpecialCharFolderRelPath := filepath.Join(specialCharFolderName, "index.tsx")
   329  	fileInsideSpecialCharFolderFile, fileInsideSpecialCharFolderStat, err := createAndStat(fileInsideSpecialCharFolderRelPath, tempDirectoryName, fs)
   330  	if err != nil {
   331  		t.Errorf("unexpected error: %v", err)
   332  	}
   333  	fileInsideSpecialCharFolderAbsPath := fileInsideSpecialCharFolderFile.Name()
   334  	specialCharFolderStat, err := fs.Stat(specialCharFolderPath)
   335  	if err != nil {
   336  		t.Errorf("unexpected error: %v", err)
   337  	}
   338  
   339  	defer os.RemoveAll(tempDirectoryName)
   340  
   341  	normalFileMap := map[string]FileData{
   342  		readmeFileName: {
   343  			Size:             readmeFileStat.Size(),
   344  			LastModifiedDate: readmeFileStat.ModTime(),
   345  		},
   346  		jsFileName: {
   347  			Size:             jsFileStat.Size(),
   348  			LastModifiedDate: jsFileStat.ModTime(),
   349  		},
   350  		viewsFolderName: {
   351  			Size:             viewsFolderStat.Size(),
   352  			LastModifiedDate: viewsFolderStat.ModTime(),
   353  		},
   354  		htmlRelFilePath: {
   355  			Size:             htmlFileStat.Size(),
   356  			LastModifiedDate: htmlFileStat.ModTime(),
   357  		},
   358  		targetFolderRelPath: {
   359  			Size:             targetFolderStat.Size(),
   360  			LastModifiedDate: targetFolderStat.ModTime(),
   361  		},
   362  		targetFileRelPath: {
   363  			Size:             targetFileStat.Size(),
   364  			LastModifiedDate: targetFileStat.ModTime(),
   365  		},
   366  		specialCharFolderName: {
   367  			Size:             specialCharFolderStat.Size(),
   368  			LastModifiedDate: specialCharFolderStat.ModTime(),
   369  		},
   370  		fileInsideSpecialCharFolderRelPath: {
   371  			Size:             fileInsideSpecialCharFolderStat.Size(),
   372  			LastModifiedDate: fileInsideSpecialCharFolderStat.ModTime(),
   373  		},
   374  	}
   375  
   376  	type args struct {
   377  		directory         string
   378  		srcBase           string
   379  		srcFile           string
   380  		destBase          string
   381  		destFile          string
   382  		ignoreRules       []string
   383  		remoteDirectories map[string]string
   384  		existingFileIndex FileIndex
   385  	}
   386  	tests := []struct {
   387  		name     string
   388  		args     args
   389  		want     IndexerRet
   390  		emptyDir bool
   391  		wantErr  bool
   392  	}{
   393  		{
   394  			name: "case 1: existing index is empty",
   395  			args: args{
   396  				directory:         tempDirectoryName,
   397  				srcBase:           tempDirectoryName,
   398  				ignoreRules:       []string{},
   399  				remoteDirectories: map[string]string{},
   400  				existingFileIndex: FileIndex{},
   401  			},
   402  			want: IndexerRet{
   403  				FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
   404  				NewFileMap:   normalFileMap,
   405  			},
   406  			wantErr: false,
   407  		},
   408  		{
   409  			name: "case 2: existing index exists and no file or folder changes occurs",
   410  			args: args{
   411  				directory:         tempDirectoryName,
   412  				srcBase:           tempDirectoryName,
   413  				ignoreRules:       []string{},
   414  				remoteDirectories: map[string]string{},
   415  				existingFileIndex: FileIndex{
   416  					Files: normalFileMap,
   417  				},
   418  			},
   419  			want: IndexerRet{
   420  				NewFileMap: normalFileMap,
   421  			},
   422  			wantErr: false,
   423  		},
   424  		{
   425  			name: "case 3: file size changed",
   426  			args: args{
   427  				directory:         tempDirectoryName,
   428  				srcBase:           tempDirectoryName,
   429  				ignoreRules:       []string{},
   430  				remoteDirectories: map[string]string{},
   431  				existingFileIndex: FileIndex{
   432  					Files: map[string]FileData{
   433  						readmeFileName: {
   434  							Size:             readmeFileStat.Size() + 100,
   435  							LastModifiedDate: readmeFileStat.ModTime(),
   436  						},
   437  						jsFileName:                         normalFileMap[jsFileName],
   438  						viewsFolderName:                    normalFileMap[viewsFolderName],
   439  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
   440  						targetFolderRelPath:                normalFileMap[targetFolderRelPath],
   441  						targetFileRelPath:                  normalFileMap[targetFileRelPath],
   442  						specialCharFolderName:              normalFileMap[specialCharFolderName],
   443  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   444  					},
   445  				},
   446  			},
   447  			want: IndexerRet{
   448  				FilesChanged: []string{readmeFileAbsPath},
   449  				NewFileMap:   normalFileMap,
   450  			},
   451  			wantErr: false,
   452  		},
   453  		{
   454  			name: "case 4: folder size changed",
   455  			args: args{
   456  				directory:         tempDirectoryName,
   457  				srcBase:           tempDirectoryName,
   458  				ignoreRules:       []string{},
   459  				remoteDirectories: map[string]string{},
   460  				existingFileIndex: FileIndex{
   461  					Files: map[string]FileData{
   462  						readmeFileName: normalFileMap[readmeFileName],
   463  						jsFileName:     normalFileMap[jsFileName],
   464  						viewsFolderName: {
   465  							Size:             viewsFolderStat.Size() + 100,
   466  							LastModifiedDate: viewsFolderStat.ModTime(),
   467  						},
   468  						htmlRelFilePath:     normalFileMap[htmlRelFilePath],
   469  						targetFolderRelPath: normalFileMap[targetFolderRelPath],
   470  						targetFileRelPath:   normalFileMap[targetFileRelPath],
   471  						specialCharFolderName: {
   472  							Size:             specialCharFolderStat.Size() + 100,
   473  							LastModifiedDate: specialCharFolderStat.ModTime(),
   474  						},
   475  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   476  					},
   477  				},
   478  			},
   479  			want: IndexerRet{
   480  				FilesChanged: []string{viewsFolderPath, specialCharFolderPath},
   481  				NewFileMap:   normalFileMap,
   482  			},
   483  			wantErr: false,
   484  		},
   485  		{
   486  			name: "case 5: file modified",
   487  			args: args{
   488  				directory:         tempDirectoryName,
   489  				srcBase:           tempDirectoryName,
   490  				ignoreRules:       []string{},
   491  				remoteDirectories: map[string]string{},
   492  				existingFileIndex: FileIndex{
   493  					Files: map[string]FileData{
   494  						readmeFileName: {
   495  							Size:             readmeFileStat.Size(),
   496  							LastModifiedDate: readmeFileStat.ModTime().Add(100),
   497  						},
   498  						jsFileName:                         normalFileMap[jsFileName],
   499  						viewsFolderName:                    normalFileMap[viewsFolderName],
   500  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
   501  						targetFolderRelPath:                normalFileMap[targetFolderRelPath],
   502  						targetFileRelPath:                  normalFileMap[targetFileRelPath],
   503  						specialCharFolderName:              normalFileMap[specialCharFolderName],
   504  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   505  					},
   506  				},
   507  			},
   508  			want: IndexerRet{
   509  				FilesChanged: []string{readmeFileAbsPath},
   510  				NewFileMap:   normalFileMap,
   511  			},
   512  			wantErr: false,
   513  		},
   514  		{
   515  			name: "case 6: folder modified",
   516  			args: args{
   517  				directory:         tempDirectoryName,
   518  				srcBase:           tempDirectoryName,
   519  				ignoreRules:       []string{},
   520  				remoteDirectories: map[string]string{},
   521  				existingFileIndex: FileIndex{
   522  					Files: map[string]FileData{
   523  						readmeFileName: normalFileMap[readmeFileName],
   524  						jsFileName:     normalFileMap[jsFileName],
   525  						viewsFolderName: {
   526  							Size:             viewsFolderStat.Size(),
   527  							LastModifiedDate: viewsFolderStat.ModTime().Add(100),
   528  						},
   529  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
   530  						targetFolderRelPath:                normalFileMap[targetFolderRelPath],
   531  						targetFileRelPath:                  normalFileMap[targetFileRelPath],
   532  						specialCharFolderName:              normalFileMap[specialCharFolderName],
   533  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   534  					},
   535  				},
   536  			},
   537  			want: IndexerRet{
   538  				FilesChanged: []string{viewsFolderPath},
   539  				NewFileMap:   normalFileMap,
   540  			},
   541  			wantErr: false,
   542  		},
   543  		{
   544  			name: "case 7: both file and folder modified",
   545  			args: args{
   546  				directory:         tempDirectoryName,
   547  				srcBase:           tempDirectoryName,
   548  				ignoreRules:       []string{},
   549  				remoteDirectories: map[string]string{},
   550  				existingFileIndex: FileIndex{
   551  					Files: map[string]FileData{
   552  						readmeFileName: {
   553  							Size:             readmeFileStat.Size() + 100,
   554  							LastModifiedDate: readmeFileStat.ModTime(),
   555  						},
   556  						jsFileName: normalFileMap[jsFileName],
   557  						viewsFolderName: {
   558  							Size:             viewsFolderStat.Size(),
   559  							LastModifiedDate: viewsFolderStat.ModTime().Add(100),
   560  						},
   561  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
   562  						targetFolderRelPath:                normalFileMap[targetFolderRelPath],
   563  						targetFileRelPath:                  normalFileMap[targetFileRelPath],
   564  						specialCharFolderName:              normalFileMap[specialCharFolderName],
   565  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   566  					},
   567  				},
   568  			},
   569  			want: IndexerRet{
   570  				FilesChanged: []string{readmeFileAbsPath, viewsFolderPath},
   571  				NewFileMap:   normalFileMap,
   572  			},
   573  			wantErr: false,
   574  		},
   575  
   576  		{
   577  			name: "case 8: ignore file with changes if remote exists",
   578  			args: args{
   579  				directory:   tempDirectoryName,
   580  				srcBase:     tempDirectoryName,
   581  				ignoreRules: []string{},
   582  				remoteDirectories: map[string]string{
   583  					htmlRelFilePath: filepath.Join("new", "Folder", "views.html"),
   584  				},
   585  				existingFileIndex: FileIndex{
   586  					Files: normalFileMap,
   587  				},
   588  			},
   589  			want: IndexerRet{
   590  				NewFileMap: map[string]FileData{
   591  					readmeFileStat.Name(): {
   592  						Size:             readmeFileStat.Size(),
   593  						LastModifiedDate: readmeFileStat.ModTime(),
   594  						RemoteAttribute:  "README.txt",
   595  					},
   596  					jsFileName: {
   597  						Size:             jsFileStat.Size(),
   598  						LastModifiedDate: jsFileStat.ModTime(),
   599  						RemoteAttribute:  "red.js",
   600  					},
   601  					viewsFolderName: {
   602  						Size:             viewsFolderStat.Size(),
   603  						LastModifiedDate: viewsFolderStat.ModTime(),
   604  						RemoteAttribute:  "views",
   605  					},
   606  					targetFolderRelPath: {
   607  						Size:             targetFolderStat.Size(),
   608  						LastModifiedDate: targetFolderStat.ModTime(),
   609  						RemoteAttribute:  targetFolderRelPath,
   610  					},
   611  					targetFileRelPath: {
   612  						Size:             targetFileStat.Size(),
   613  						LastModifiedDate: targetFileStat.ModTime(),
   614  						RemoteAttribute:  targetFileRelPath,
   615  					},
   616  					specialCharFolderName: {
   617  						Size:             specialCharFolderStat.Size(),
   618  						LastModifiedDate: specialCharFolderStat.ModTime(),
   619  						RemoteAttribute:  specialCharFolderName,
   620  					},
   621  					fileInsideSpecialCharFolderRelPath: {
   622  						Size:             fileInsideSpecialCharFolderStat.Size(),
   623  						LastModifiedDate: fileInsideSpecialCharFolderStat.ModTime(),
   624  						RemoteAttribute:  fileInsideSpecialCharFolderRelPath,
   625  					},
   626  				},
   627  			},
   628  			wantErr: false,
   629  		},
   630  		{
   631  			name: "case 9: remote removed for a file containing different remote destination",
   632  			args: args{
   633  				directory:         tempDirectoryName,
   634  				srcBase:           tempDirectoryName,
   635  				ignoreRules:       []string{},
   636  				remoteDirectories: map[string]string{},
   637  				existingFileIndex: FileIndex{
   638  					Files: map[string]FileData{
   639  						readmeFileName:  normalFileMap[readmeFileName],
   640  						jsFileName:      normalFileMap[jsFileName],
   641  						viewsFolderName: normalFileMap[viewsFolderName],
   642  						htmlRelFilePath: {
   643  							Size:             htmlFileStat.Size(),
   644  							LastModifiedDate: htmlFileStat.ModTime(),
   645  							RemoteAttribute:  filepath.Join("new", "Folder", "views.html"),
   646  						},
   647  						targetFolderRelPath:                normalFileMap[targetFolderRelPath],
   648  						targetFileRelPath:                  normalFileMap[targetFileRelPath],
   649  						specialCharFolderName:              normalFileMap[specialCharFolderName],
   650  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   651  					},
   652  				},
   653  			},
   654  			want: IndexerRet{
   655  				FilesChanged:  []string{htmlFileAbsPath},
   656  				RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder", "views.html")},
   657  				NewFileMap:    normalFileMap,
   658  			},
   659  			wantErr: false,
   660  		},
   661  		{
   662  			name: "case 10: remote removed for a folder containing different remote destination",
   663  			args: args{
   664  				directory:         tempDirectoryName,
   665  				srcBase:           tempDirectoryName,
   666  				ignoreRules:       []string{},
   667  				remoteDirectories: map[string]string{},
   668  				existingFileIndex: FileIndex{
   669  					Files: map[string]FileData{
   670  						readmeFileName: normalFileMap[readmeFileName],
   671  						jsFileName:     normalFileMap[jsFileName],
   672  						viewsFolderName: {
   673  							Size:             viewsFolderStat.Size(),
   674  							LastModifiedDate: viewsFolderStat.ModTime(),
   675  							RemoteAttribute:  "new/Folder/views",
   676  						},
   677  						htmlRelFilePath: {
   678  							Size:             htmlFileStat.Size(),
   679  							LastModifiedDate: htmlFileStat.ModTime(),
   680  							RemoteAttribute:  filepath.Join("new", "Folder", "views.html"),
   681  						},
   682  						targetFolderRelPath: {
   683  							Size:             htmlFileStat.Size(),
   684  							LastModifiedDate: htmlFileStat.ModTime(),
   685  							RemoteAttribute:  "new/Folder/target",
   686  						},
   687  						targetFileRelPath: {
   688  							Size:             htmlFileStat.Size(),
   689  							LastModifiedDate: htmlFileStat.ModTime(),
   690  							RemoteAttribute:  "new/Folder/target/someFile.txt",
   691  						},
   692  						specialCharFolderName:              normalFileMap[specialCharFolderName],
   693  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   694  					},
   695  				},
   696  			},
   697  			want: IndexerRet{
   698  				FilesChanged:  []string{viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath},
   699  				RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), "new/Folder/target", "new/Folder/target/someFile.txt", filepath.Join("new", "Folder", "views.html"), "new/Folder/views"},
   700  				NewFileMap:    normalFileMap,
   701  			},
   702  			wantErr: false,
   703  		},
   704  		{
   705  			name: "case 11: folder remote changed to local path",
   706  			args: args{
   707  				directory:   tempDirectoryName,
   708  				srcBase:     tempDirectoryName,
   709  				srcFile:     viewsFolderName,
   710  				destFile:    viewsFolderName,
   711  				ignoreRules: []string{},
   712  				remoteDirectories: map[string]string{
   713  					viewsFolderName: viewsFolderName,
   714  				},
   715  				existingFileIndex: FileIndex{
   716  					Files: map[string]FileData{
   717  						viewsFolderName: {
   718  							Size:             viewsFolderStat.Size(),
   719  							LastModifiedDate: viewsFolderStat.ModTime(),
   720  							RemoteAttribute:  "new/Folder/views",
   721  						},
   722  						htmlRelFilePath: {
   723  							Size:             htmlFileStat.Size(),
   724  							LastModifiedDate: htmlFileStat.ModTime(),
   725  							RemoteAttribute:  "new/Folder/views/view.html",
   726  						},
   727  					},
   728  				},
   729  			},
   730  			want: IndexerRet{
   731  				FilesChanged:  []string{viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath},
   732  				RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), "new/Folder/views", "new/Folder/views/view.html"},
   733  				NewFileMap: map[string]FileData{
   734  					viewsFolderName: {
   735  						Size:             viewsFolderStat.Size(),
   736  						LastModifiedDate: viewsFolderStat.ModTime(),
   737  						RemoteAttribute:  filepath.ToSlash(viewsFolderName),
   738  					},
   739  					targetFolderRelPath: {
   740  						Size:             targetFolderStat.Size(),
   741  						LastModifiedDate: targetFolderStat.ModTime(),
   742  						RemoteAttribute:  filepath.ToSlash(targetFolderRelPath),
   743  					},
   744  					targetFileRelPath: {
   745  						Size:             targetFileStat.Size(),
   746  						LastModifiedDate: targetFileStat.ModTime(),
   747  						RemoteAttribute:  filepath.ToSlash(targetFileRelPath),
   748  					},
   749  					htmlRelFilePath: {
   750  						Size:             htmlFileStat.Size(),
   751  						LastModifiedDate: htmlFileStat.ModTime(),
   752  						RemoteAttribute:  filepath.ToSlash(htmlRelFilePath),
   753  					}},
   754  			},
   755  			wantErr: false,
   756  		},
   757  
   758  		{
   759  			name: "case 12: only a single file is checked and others are ignored",
   760  			args: args{
   761  				directory:         tempDirectoryName,
   762  				srcBase:           filepath.Join(tempDirectoryName, "views"),
   763  				srcFile:           "view.html",
   764  				ignoreRules:       []string{},
   765  				remoteDirectories: map[string]string{},
   766  				existingFileIndex: FileIndex{
   767  					Files: map[string]FileData{
   768  						htmlRelFilePath: {
   769  							Size:             htmlFileStat.Size() + 100,
   770  							LastModifiedDate: htmlFileStat.ModTime(),
   771  							RemoteAttribute:  "",
   772  						},
   773  						readmeFileStat.Name(): {
   774  							Size:             readmeFileStat.Size() + 100,
   775  							LastModifiedDate: readmeFileStat.ModTime(),
   776  						},
   777  						jsFileName:      normalFileMap["red.js"],
   778  						viewsFolderName: normalFileMap["views"],
   779  					},
   780  				},
   781  			},
   782  			want: IndexerRet{
   783  				FilesChanged: []string{htmlFileAbsPath},
   784  				NewFileMap: map[string]FileData{
   785  					htmlRelFilePath: {
   786  						Size:             htmlFileStat.Size(),
   787  						LastModifiedDate: htmlFileStat.ModTime(),
   788  					},
   789  				},
   790  			},
   791  			wantErr: false,
   792  		},
   793  		{
   794  			name: "case 13: only a single file with a different remote is checked",
   795  			args: args{
   796  				directory:   tempDirectoryName,
   797  				srcBase:     tempDirectoryName,
   798  				srcFile:     "README.txt",
   799  				ignoreRules: []string{},
   800  				remoteDirectories: map[string]string{
   801  					readmeFileStat.Name(): "new/Folder/text/README.txt",
   802  				},
   803  				existingFileIndex: FileIndex{
   804  					Files: normalFileMap,
   805  				},
   806  			},
   807  			want: IndexerRet{
   808  				FilesChanged:  []string{readmeFileAbsPath},
   809  				RemoteDeleted: []string{filepath.ToSlash(readmeFileStat.Name())},
   810  				NewFileMap: map[string]FileData{
   811  					readmeFileStat.Name(): {
   812  						Size:             readmeFileStat.Size(),
   813  						LastModifiedDate: readmeFileStat.ModTime(),
   814  						RemoteAttribute:  "new/Folder/text/README.txt",
   815  					}},
   816  			},
   817  			wantErr: false,
   818  		},
   819  		{
   820  			name: "case 14: only a single file is checked with a remote removed",
   821  			args: args{
   822  				directory:         tempDirectoryName,
   823  				srcBase:           tempDirectoryName,
   824  				srcFile:           "README.txt",
   825  				destBase:          tempDirectoryName,
   826  				destFile:          "README.txt",
   827  				ignoreRules:       []string{},
   828  				remoteDirectories: map[string]string{},
   829  				existingFileIndex: FileIndex{
   830  					Files: map[string]FileData{
   831  						htmlRelFilePath: normalFileMap["views/view.html"],
   832  						readmeFileStat.Name(): {
   833  							Size:             readmeFileStat.Size(),
   834  							LastModifiedDate: readmeFileStat.ModTime(),
   835  							RemoteAttribute:  "new/Folder/text/README.txt",
   836  						},
   837  						jsFileName:      normalFileMap[jsFileName],
   838  						viewsFolderName: normalFileMap[viewsFolderName],
   839  					},
   840  				},
   841  			},
   842  			want: IndexerRet{
   843  				FilesChanged:  []string{readmeFileAbsPath},
   844  				RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder", "text"), filepath.Join("new", "Folder", "text", readmeFileName)},
   845  				NewFileMap: map[string]FileData{
   846  					readmeFileName: normalFileMap[readmeFileName],
   847  				},
   848  			},
   849  			wantErr: false,
   850  		},
   851  		{
   852  			name: "case 15: only a single file is checked with the same remote path earlier",
   853  			args: args{
   854  				directory:         tempDirectoryName,
   855  				srcBase:           tempDirectoryName,
   856  				srcFile:           "README.txt",
   857  				destBase:          tempDirectoryName,
   858  				destFile:          "README.txt",
   859  				ignoreRules:       []string{},
   860  				remoteDirectories: map[string]string{},
   861  				existingFileIndex: FileIndex{
   862  					Files: map[string]FileData{
   863  						htmlRelFilePath: normalFileMap["views/view.html"],
   864  						readmeFileStat.Name(): {
   865  							Size:             readmeFileStat.Size(),
   866  							LastModifiedDate: readmeFileStat.ModTime(),
   867  							RemoteAttribute:  "README.txt",
   868  						},
   869  						jsFileName:      normalFileMap["red.js"],
   870  						viewsFolderName: normalFileMap["views"],
   871  					},
   872  				},
   873  			},
   874  			want: IndexerRet{
   875  				NewFileMap: map[string]FileData{
   876  					readmeFileStat.Name(): normalFileMap["README.txt"],
   877  				},
   878  			},
   879  			wantErr: false,
   880  		},
   881  		{
   882  			name: "case 16: only a single file is checked and there is no modification",
   883  			args: args{
   884  				directory:   tempDirectoryName,
   885  				srcBase:     viewsFolderPath,
   886  				srcFile:     "view.html",
   887  				ignoreRules: []string{},
   888  				remoteDirectories: map[string]string{
   889  					htmlRelFilePath: "new/views/view.html",
   890  				},
   891  				existingFileIndex: FileIndex{
   892  					Files: map[string]FileData{
   893  						htmlRelFilePath: {
   894  							Size:             htmlFileStat.Size(),
   895  							LastModifiedDate: htmlFileStat.ModTime(),
   896  							RemoteAttribute:  "new/views/view.html",
   897  						},
   898  					},
   899  				},
   900  			},
   901  			want: IndexerRet{
   902  				NewFileMap: map[string]FileData{
   903  					htmlRelFilePath: {
   904  						Size:             htmlFileStat.Size(),
   905  						LastModifiedDate: htmlFileStat.ModTime(),
   906  						RemoteAttribute:  "new/views/view.html",
   907  					},
   908  				},
   909  			},
   910  			wantErr: false,
   911  		},
   912  		{
   913  			name: "case 17: file remote changed to local path",
   914  			args: args{
   915  				directory:   tempDirectoryName,
   916  				srcBase:     tempDirectoryName,
   917  				srcFile:     "README.txt",
   918  				ignoreRules: []string{},
   919  				remoteDirectories: map[string]string{
   920  					readmeFileName: readmeFileName,
   921  				},
   922  				existingFileIndex: FileIndex{
   923  					Files: map[string]FileData{
   924  						readmeFileName: {
   925  							Size:             readmeFileStat.Size(),
   926  							LastModifiedDate: readmeFileStat.ModTime(),
   927  							RemoteAttribute:  "new/Folder/README.txt",
   928  						},
   929  					},
   930  				},
   931  			},
   932  			want: IndexerRet{
   933  				FilesChanged:  []string{readmeFileAbsPath},
   934  				RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder", "README.txt")},
   935  				NewFileMap: map[string]FileData{
   936  					readmeFileName: {
   937  						Size:             readmeFileStat.Size(),
   938  						LastModifiedDate: readmeFileStat.ModTime(),
   939  						RemoteAttribute:  readmeFileStat.Name(),
   940  					}},
   941  			},
   942  			wantErr: false,
   943  		},
   944  
   945  		{
   946  			name: "case 18: file doesn't exist",
   947  			args: args{
   948  				directory:         tempDirectoryName,
   949  				srcBase:           viewsFolderPath,
   950  				srcFile:           "views.html",
   951  				ignoreRules:       []string{},
   952  				remoteDirectories: map[string]string{},
   953  				existingFileIndex: FileIndex{},
   954  			},
   955  			want: IndexerRet{
   956  				NewFileMap: map[string]FileData{},
   957  			},
   958  			wantErr: true,
   959  		},
   960  		{
   961  			name: "case 19: folder doesn't exist",
   962  			args: args{
   963  				directory:         tempDirectoryName,
   964  				srcBase:           tempDirectoryName + "blah",
   965  				ignoreRules:       []string{},
   966  				remoteDirectories: map[string]string{},
   967  				existingFileIndex: FileIndex{
   968  					Files: map[string]FileData{},
   969  				},
   970  			},
   971  			want: IndexerRet{
   972  				NewFileMap: map[string]FileData{},
   973  			},
   974  			wantErr: true,
   975  		},
   976  
   977  		{
   978  			name: "case 20: ignore given file",
   979  			args: args{
   980  				directory:         tempDirectoryName,
   981  				srcBase:           tempDirectoryName,
   982  				ignoreRules:       []string{"*.html"},
   983  				remoteDirectories: map[string]string{},
   984  				existingFileIndex: FileIndex{
   985  					Files: map[string]FileData{},
   986  				},
   987  			},
   988  			want: IndexerRet{
   989  				FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, targetFolderPath, targetFilePath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
   990  				NewFileMap: map[string]FileData{
   991  					jsFileName:                         normalFileMap["red.js"],
   992  					viewsFolderName:                    normalFileMap["views"],
   993  					readmeFileStat.Name():              normalFileMap["README.txt"],
   994  					targetFolderRelPath:                normalFileMap[targetFolderRelPath],
   995  					targetFileRelPath:                  normalFileMap[targetFileRelPath],
   996  					specialCharFolderName:              normalFileMap[specialCharFolderName],
   997  					fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
   998  				},
   999  			},
  1000  			wantErr: false,
  1001  		},
  1002  		{
  1003  			name: "case 21: ignore given folder",
  1004  			args: args{
  1005  				directory:         tempDirectoryName,
  1006  				srcBase:           tempDirectoryName,
  1007  				ignoreRules:       []string{viewsFolderName},
  1008  				remoteDirectories: map[string]string{},
  1009  				existingFileIndex: FileIndex{
  1010  					Files: map[string]FileData{},
  1011  				},
  1012  			},
  1013  			want: IndexerRet{
  1014  				FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
  1015  				NewFileMap: map[string]FileData{
  1016  					jsFileName:                         normalFileMap["red.js"],
  1017  					readmeFileStat.Name():              normalFileMap["README.txt"],
  1018  					specialCharFolderName:              normalFileMap[specialCharFolderName],
  1019  					fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1020  				},
  1021  			},
  1022  			wantErr: false,
  1023  		},
  1024  
  1025  		{
  1026  			name: "case 22: only empty Dir with different remote location is checked",
  1027  			args: args{
  1028  				directory:   tempDirectoryName,
  1029  				srcBase:     filepath.Join(tempDirectoryName, "emptyDir"),
  1030  				srcFile:     "",
  1031  				destBase:    filepath.Join(tempDirectoryName, "emptyDir"),
  1032  				destFile:    "",
  1033  				ignoreRules: []string{},
  1034  				remoteDirectories: map[string]string{
  1035  					"emptyDir": "new/Folder/",
  1036  				},
  1037  				existingFileIndex: FileIndex{
  1038  					Files: normalFileMap,
  1039  				},
  1040  			},
  1041  			emptyDir: true,
  1042  			want: IndexerRet{
  1043  				FilesChanged: []string{filepath.Join(tempDirectoryName, "emptyDir")},
  1044  				NewFileMap:   map[string]FileData{},
  1045  			},
  1046  			wantErr: false,
  1047  		},
  1048  		{
  1049  			name: "case 23: folder containing a empty directory",
  1050  			args: args{
  1051  				directory:         tempDirectoryName,
  1052  				srcBase:           tempDirectoryName,
  1053  				ignoreRules:       []string{},
  1054  				remoteDirectories: map[string]string{},
  1055  				existingFileIndex: FileIndex{
  1056  					Files: map[string]FileData{},
  1057  				},
  1058  			},
  1059  			emptyDir: true,
  1060  			want: IndexerRet{
  1061  				FilesChanged: []string{readmeFileAbsPath, filepath.Join(tempDirectoryName, "emptyDir"), jsFileAbsPath, viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
  1062  				NewFileMap:   normalFileMap,
  1063  			},
  1064  			wantErr: false,
  1065  		},
  1066  		{
  1067  			name: "Case 24: subfolder is ignored",
  1068  			args: args{
  1069  				directory:         tempDirectoryName,
  1070  				srcBase:           tempDirectoryName,
  1071  				ignoreRules:       []string{"target/"},
  1072  				remoteDirectories: map[string]string{},
  1073  				existingFileIndex: FileIndex{
  1074  					Files: map[string]FileData{},
  1075  				},
  1076  			},
  1077  			want: IndexerRet{
  1078  				FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, targetFolderPath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
  1079  				NewFileMap: map[string]FileData{
  1080  					readmeFileName:                     normalFileMap[readmeFileName],
  1081  					jsFileName:                         normalFileMap[jsFileName],
  1082  					viewsFolderName:                    normalFileMap[viewsFolderName],
  1083  					htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1084  					targetFolderRelPath:                normalFileMap[targetFolderRelPath],
  1085  					specialCharFolderName:              normalFileMap[specialCharFolderName],
  1086  					fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1087  				},
  1088  			},
  1089  		},
  1090  	}
  1091  	for _, tt := range tests {
  1092  		t.Run(tt.name, func(t *testing.T) {
  1093  			if tt.emptyDir {
  1094  				emptyDirPath := filepath.Join(tempDirectoryName, "emptyDir")
  1095  				err = fs.MkdirAll(emptyDirPath, 0755)
  1096  				if err != nil {
  1097  					t.Errorf("unexpected error: %v", err)
  1098  				}
  1099  
  1100  				defer func(name string) {
  1101  					err := os.Remove(name)
  1102  					if err != nil {
  1103  						t.Errorf("enexpected error: %v", err)
  1104  					}
  1105  				}(emptyDirPath)
  1106  
  1107  				emptyDirStat, err := fs.Stat(emptyDirPath)
  1108  				if err != nil {
  1109  					t.Errorf("enexpected error: %v", err)
  1110  				}
  1111  
  1112  				tt.want.NewFileMap[emptyDirStat.Name()] = FileData{
  1113  					Size:             emptyDirStat.Size(),
  1114  					LastModifiedDate: emptyDirStat.ModTime(),
  1115  					RemoteAttribute:  tt.args.remoteDirectories[emptyDirStat.Name()],
  1116  				}
  1117  			}
  1118  			pathsOptions := recursiveCheckerPathOptions{tt.args.directory, tt.args.srcBase, tt.args.srcFile, tt.args.destBase, tt.args.destFile}
  1119  			got, err := recursiveChecker(pathsOptions, tt.args.ignoreRules, tt.args.remoteDirectories, tt.args.existingFileIndex)
  1120  			if (err != nil) != tt.wantErr {
  1121  				t.Errorf("recursiveChecker() error = %v, wantErr %v", err, tt.wantErr)
  1122  				return
  1123  			}
  1124  
  1125  			if err != nil && tt.wantErr {
  1126  				return
  1127  			}
  1128  
  1129  			sortOpt := cmpopts.SortSlices(func(x, y string) bool {
  1130  				return x < y
  1131  			})
  1132  			if diff := cmp.Diff(tt.want.FilesChanged, got.FilesChanged, sortOpt); diff != "" {
  1133  				t.Errorf("recursiveChecker() FilesChanged mismatch (-want +got):\n%s", diff)
  1134  			}
  1135  
  1136  			if diff := cmp.Diff(tt.want.FilesDeleted, got.FilesDeleted, sortOpt); diff != "" {
  1137  				t.Errorf("recursiveChecker() FilesDeleted mismatch (-want +got):\n%s", diff)
  1138  			}
  1139  
  1140  			if diff := cmp.Diff(tt.want.RemoteDeleted, got.RemoteDeleted, sortOpt); diff != "" {
  1141  				t.Errorf("recursiveChecker() RemoteDeleted mismatch (-want +got):\n%s", diff)
  1142  			}
  1143  
  1144  			if diff := cmp.Diff(tt.want.NewFileMap, got.NewFileMap); diff != "" {
  1145  				t.Errorf("recursiveChecker() NewFileMap mismatch (-want +got):\n%s", diff)
  1146  			}
  1147  		})
  1148  	}
  1149  }
  1150  
  1151  func Test_runIndexerWithExistingFileIndex(t *testing.T) {
  1152  	fs := filesystem.DefaultFs{}
  1153  
  1154  	tempDirectoryName, err := fs.TempDir(os.TempDir(), "dir0")
  1155  	if err != nil {
  1156  		t.Errorf("unexpected error: %v", err)
  1157  	}
  1158  
  1159  	jsFileName := "red.js"
  1160  	jsFile, jsFileStat, err := createAndStat(jsFileName, tempDirectoryName, fs)
  1161  	if err != nil {
  1162  		t.Errorf("unexpected error: %v", err)
  1163  	}
  1164  	jsFileAbsPath := jsFile.Name()
  1165  
  1166  	readmeFileName := "README.txt"
  1167  	readmeFile, readmeFileStat, err := createAndStat(readmeFileName, tempDirectoryName, fs)
  1168  	if err != nil {
  1169  		t.Errorf("unexpected error: %v", err)
  1170  	}
  1171  	readmeFileAbsPath := readmeFile.Name()
  1172  
  1173  	viewsFolderName := "views"
  1174  	viewsFolderPath := filepath.Join(tempDirectoryName, viewsFolderName)
  1175  	err = fs.MkdirAll(viewsFolderPath, 0755)
  1176  	if err != nil {
  1177  		t.Errorf("unexpected error: %v", err)
  1178  	}
  1179  
  1180  	htmlRelFilePath := filepath.Join(viewsFolderName, "view.html")
  1181  	htmlFile, htmlFileStat, err := createAndStat(filepath.Join("views", "view.html"), tempDirectoryName, fs)
  1182  	if err != nil {
  1183  		t.Errorf("unexpected error: %v", err)
  1184  	}
  1185  	htmlFileAbsPath := htmlFile.Name()
  1186  
  1187  	viewsFolderStat, err := fs.Stat(filepath.Join(tempDirectoryName, viewsFolderName))
  1188  	if err != nil {
  1189  		t.Errorf("unexpected error: %v", err)
  1190  	}
  1191  
  1192  	specialCharFolderName := "[devfile-registry]"
  1193  	specialCharFolderPath := filepath.Join(tempDirectoryName, specialCharFolderName)
  1194  	err = fs.MkdirAll(specialCharFolderPath, 0755)
  1195  	if err != nil {
  1196  		t.Errorf("unexpected error: %v", err)
  1197  	}
  1198  
  1199  	fileInsideSpecialCharFolderRelPath := filepath.Join(specialCharFolderName, "index.tsx")
  1200  	fileInsideSpecialCharFolderFile, fileInsideSpecialCharFolderFileStat, err := createAndStat(fileInsideSpecialCharFolderRelPath, tempDirectoryName, fs)
  1201  	if err != nil {
  1202  		t.Errorf("unexpected error: %v", err)
  1203  	}
  1204  	fileInsideSpecialCharFolderAbsPath := fileInsideSpecialCharFolderFile.Name()
  1205  
  1206  	specialCharFolderStat, err := fs.Stat(specialCharFolderPath)
  1207  	if err != nil {
  1208  		t.Errorf("unexpected error: %v", err)
  1209  	}
  1210  
  1211  	defer os.RemoveAll(tempDirectoryName)
  1212  
  1213  	normalFileMap := map[string]FileData{
  1214  		readmeFileName: {
  1215  			Size:             readmeFileStat.Size(),
  1216  			LastModifiedDate: readmeFileStat.ModTime(),
  1217  		},
  1218  		jsFileName: {
  1219  			Size:             jsFileStat.Size(),
  1220  			LastModifiedDate: jsFileStat.ModTime(),
  1221  		},
  1222  		viewsFolderName: {
  1223  			Size:             viewsFolderStat.Size(),
  1224  			LastModifiedDate: viewsFolderStat.ModTime(),
  1225  		},
  1226  		htmlRelFilePath: {
  1227  			Size:             htmlFileStat.Size(),
  1228  			LastModifiedDate: htmlFileStat.ModTime(),
  1229  		},
  1230  		specialCharFolderName: {
  1231  			Size:             specialCharFolderStat.Size(),
  1232  			LastModifiedDate: specialCharFolderStat.ModTime(),
  1233  		},
  1234  		fileInsideSpecialCharFolderRelPath: {
  1235  			Size:             fileInsideSpecialCharFolderFileStat.Size(),
  1236  			LastModifiedDate: fileInsideSpecialCharFolderFileStat.ModTime(),
  1237  		},
  1238  	}
  1239  
  1240  	type args struct {
  1241  		directory         string
  1242  		ignoreRules       []string
  1243  		remoteDirectories map[string]string
  1244  		existingFileIndex *FileIndex
  1245  	}
  1246  	tests := []struct {
  1247  		name    string
  1248  		args    args
  1249  		wantRet IndexerRet
  1250  		wantErr bool
  1251  	}{
  1252  		{
  1253  			name: "case 1: normal directory with no existing file index data",
  1254  			args: args{
  1255  				directory:         tempDirectoryName,
  1256  				ignoreRules:       []string{},
  1257  				remoteDirectories: map[string]string{},
  1258  				existingFileIndex: &FileIndex{},
  1259  			},
  1260  			wantRet: IndexerRet{
  1261  				FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
  1262  				NewFileMap:   normalFileMap,
  1263  			},
  1264  			wantErr: false,
  1265  		},
  1266  		{
  1267  			name: "case 2: normal directory with existing file index data",
  1268  			args: args{
  1269  				directory:         tempDirectoryName,
  1270  				ignoreRules:       []string{},
  1271  				remoteDirectories: map[string]string{},
  1272  				existingFileIndex: &FileIndex{
  1273  					Files: normalFileMap,
  1274  				},
  1275  			},
  1276  			wantRet: IndexerRet{
  1277  				NewFileMap: normalFileMap,
  1278  			},
  1279  			wantErr: false,
  1280  		},
  1281  		{
  1282  			name: "case 3: normal directory with existing file index data and new files are added",
  1283  			args: args{
  1284  				directory:         tempDirectoryName,
  1285  				ignoreRules:       []string{},
  1286  				remoteDirectories: map[string]string{},
  1287  				existingFileIndex: &FileIndex{
  1288  					Files: map[string]FileData{
  1289  						htmlRelFilePath: normalFileMap[htmlRelFilePath],
  1290  					},
  1291  				},
  1292  			},
  1293  			wantRet: IndexerRet{
  1294  				FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
  1295  				NewFileMap:   normalFileMap,
  1296  			},
  1297  			wantErr: false,
  1298  		},
  1299  		{
  1300  			name: "case 4: normal directory with existing file index data and files are deleted",
  1301  			args: args{
  1302  				directory:         tempDirectoryName,
  1303  				ignoreRules:       []string{},
  1304  				remoteDirectories: map[string]string{},
  1305  				existingFileIndex: &FileIndex{
  1306  					Files: map[string]FileData{
  1307  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1308  						jsFileName:                         normalFileMap[jsFileName],
  1309  						viewsFolderName:                    normalFileMap[viewsFolderName],
  1310  						readmeFileName:                     normalFileMap[readmeFileName],
  1311  						specialCharFolderName:              normalFileMap[specialCharFolderName],
  1312  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1313  						"blah":                             {},
  1314  					},
  1315  				},
  1316  			},
  1317  			wantRet: IndexerRet{
  1318  				FilesDeleted: []string{"blah"},
  1319  				NewFileMap:   normalFileMap,
  1320  			},
  1321  			wantErr: false,
  1322  		},
  1323  
  1324  		{
  1325  			name: "case 5: with remote directories and no existing file index",
  1326  			args: args{
  1327  				directory:         tempDirectoryName,
  1328  				ignoreRules:       []string{},
  1329  				remoteDirectories: map[string]string{viewsFolderName: filepath.Join("new", "Folder"), htmlRelFilePath: filepath.Join("new", "Folder0", "views.html")},
  1330  				existingFileIndex: &FileIndex{},
  1331  			},
  1332  			wantRet: IndexerRet{
  1333  				FilesChanged: []string{viewsFolderPath, htmlFileAbsPath},
  1334  				NewFileMap: map[string]FileData{
  1335  					htmlRelFilePath: {
  1336  						Size:             htmlFileStat.Size(),
  1337  						LastModifiedDate: htmlFileStat.ModTime(),
  1338  						RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1339  					},
  1340  					viewsFolderName: {
  1341  						Size:             viewsFolderStat.Size(),
  1342  						LastModifiedDate: viewsFolderStat.ModTime(),
  1343  						RemoteAttribute:  filepath.Join("new", "Folder"),
  1344  					},
  1345  				},
  1346  			},
  1347  			wantErr: false,
  1348  		},
  1349  		{
  1350  			name: "case 6: with remote directories and no modification",
  1351  			args: args{
  1352  				directory:         tempDirectoryName,
  1353  				ignoreRules:       []string{},
  1354  				remoteDirectories: map[string]string{htmlRelFilePath: filepath.Join("new", "Folder0", "views.html"), viewsFolderName: filepath.Join("new", "Folder")},
  1355  				existingFileIndex: &FileIndex{
  1356  					Files: map[string]FileData{
  1357  						htmlRelFilePath: {
  1358  							Size:             htmlFileStat.Size(),
  1359  							LastModifiedDate: htmlFileStat.ModTime(),
  1360  							RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1361  						},
  1362  						viewsFolderName: {
  1363  							Size:             viewsFolderStat.Size(),
  1364  							LastModifiedDate: viewsFolderStat.ModTime(),
  1365  							RemoteAttribute:  filepath.Join("new", "Folder"),
  1366  						},
  1367  					},
  1368  				},
  1369  			},
  1370  			wantRet: IndexerRet{
  1371  				NewFileMap: map[string]FileData{
  1372  					htmlRelFilePath: {
  1373  						Size:             htmlFileStat.Size(),
  1374  						LastModifiedDate: htmlFileStat.ModTime(),
  1375  						RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1376  					},
  1377  					viewsFolderName: {
  1378  						Size:             viewsFolderStat.Size(),
  1379  						LastModifiedDate: viewsFolderStat.ModTime(),
  1380  						RemoteAttribute:  filepath.Join("new", "Folder"),
  1381  					},
  1382  				},
  1383  			},
  1384  			wantErr: false,
  1385  		},
  1386  		{
  1387  			name: "case 7: with remote directories and files deleted",
  1388  			args: args{
  1389  				directory:   tempDirectoryName,
  1390  				ignoreRules: []string{},
  1391  				remoteDirectories: map[string]string{
  1392  					htmlRelFilePath: filepath.Join("new", "Folder0", "views.html"),
  1393  					viewsFolderName: filepath.Join("new", "Folder"),
  1394  				},
  1395  				existingFileIndex: &FileIndex{
  1396  					Files: map[string]FileData{
  1397  						htmlRelFilePath: {
  1398  							Size:             htmlFileStat.Size(),
  1399  							LastModifiedDate: htmlFileStat.ModTime(),
  1400  							RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1401  						},
  1402  						viewsFolderName: {
  1403  							Size:             viewsFolderStat.Size(),
  1404  							LastModifiedDate: viewsFolderStat.ModTime(),
  1405  							RemoteAttribute:  filepath.Join("new", "Folder"),
  1406  						},
  1407  						"blah": {},
  1408  					},
  1409  				},
  1410  			},
  1411  			wantRet: IndexerRet{
  1412  				FilesDeleted: []string{"blah"},
  1413  				NewFileMap: map[string]FileData{
  1414  					htmlRelFilePath: {
  1415  						Size:             htmlFileStat.Size(),
  1416  						LastModifiedDate: htmlFileStat.ModTime(),
  1417  						RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1418  					},
  1419  					viewsFolderName: {
  1420  						Size:             viewsFolderStat.Size(),
  1421  						LastModifiedDate: viewsFolderStat.ModTime(),
  1422  						RemoteAttribute:  filepath.Join("new", "Folder"),
  1423  					},
  1424  				},
  1425  			},
  1426  			wantErr: false,
  1427  		},
  1428  		{
  1429  			name: "case 8: remote changed",
  1430  			args: args{
  1431  				directory:   tempDirectoryName,
  1432  				ignoreRules: []string{},
  1433  				remoteDirectories: map[string]string{
  1434  					htmlRelFilePath: filepath.Join("new", "Folder0", "views.html"),
  1435  					viewsFolderName: filepath.Join("new", "blah", "Folder"),
  1436  				},
  1437  				existingFileIndex: &FileIndex{
  1438  					Files: map[string]FileData{
  1439  						htmlRelFilePath: {
  1440  							Size:             htmlFileStat.Size(),
  1441  							LastModifiedDate: htmlFileStat.ModTime(),
  1442  							RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1443  						},
  1444  						viewsFolderName: {
  1445  							Size:             viewsFolderStat.Size(),
  1446  							LastModifiedDate: viewsFolderStat.ModTime(),
  1447  							RemoteAttribute:  filepath.Join("new", "Folder"),
  1448  						},
  1449  					},
  1450  				},
  1451  			},
  1452  			wantRet: IndexerRet{
  1453  				FilesChanged:  []string{viewsFolderPath},
  1454  				RemoteDeleted: []string{filepath.Join("new", "Folder")},
  1455  				NewFileMap: map[string]FileData{
  1456  					htmlRelFilePath: {
  1457  						Size:             htmlFileStat.Size(),
  1458  						LastModifiedDate: htmlFileStat.ModTime(),
  1459  						RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1460  					},
  1461  					viewsFolderName: {
  1462  						Size:             viewsFolderStat.Size(),
  1463  						LastModifiedDate: viewsFolderStat.ModTime(),
  1464  						RemoteAttribute:  filepath.Join("new", "blah", "Folder"),
  1465  					},
  1466  				},
  1467  			},
  1468  			wantErr: false,
  1469  		},
  1470  		{
  1471  			name: "case 9: remote of a file removed",
  1472  			args: args{
  1473  				directory:         tempDirectoryName,
  1474  				ignoreRules:       []string{},
  1475  				remoteDirectories: map[string]string{htmlRelFilePath: filepath.Join("new", "Folder0", "views.html")},
  1476  				existingFileIndex: &FileIndex{
  1477  					Files: map[string]FileData{
  1478  						htmlRelFilePath: {
  1479  							Size:             htmlFileStat.Size(),
  1480  							LastModifiedDate: htmlFileStat.ModTime(),
  1481  							RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1482  						},
  1483  						viewsFolderName: {
  1484  							Size:             viewsFolderStat.Size(),
  1485  							LastModifiedDate: viewsFolderStat.ModTime(),
  1486  							RemoteAttribute:  filepath.Join("new", "Folder"),
  1487  						},
  1488  					},
  1489  				},
  1490  			},
  1491  			wantRet: IndexerRet{
  1492  				RemoteDeleted: []string{filepath.Join("new", "Folder")},
  1493  				NewFileMap: map[string]FileData{
  1494  					htmlRelFilePath: {
  1495  						Size:             htmlFileStat.Size(),
  1496  						LastModifiedDate: htmlFileStat.ModTime(),
  1497  						RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1498  					},
  1499  				},
  1500  			},
  1501  			wantErr: false,
  1502  		},
  1503  		{
  1504  			name: "case 10: all remotes removed",
  1505  			args: args{
  1506  				directory:         tempDirectoryName,
  1507  				ignoreRules:       []string{},
  1508  				remoteDirectories: map[string]string{},
  1509  				existingFileIndex: &FileIndex{
  1510  					Files: map[string]FileData{
  1511  						readmeFileName: {
  1512  							Size:             readmeFileStat.Size(),
  1513  							LastModifiedDate: readmeFileStat.ModTime(),
  1514  							RemoteAttribute:  readmeFileStat.Name(),
  1515  						},
  1516  						htmlRelFilePath: {
  1517  							Size:             htmlFileStat.Size(),
  1518  							LastModifiedDate: htmlFileStat.ModTime(),
  1519  							RemoteAttribute:  filepath.Join("new", "Folder0", "views.html"),
  1520  						},
  1521  						viewsFolderName: {
  1522  							Size:             viewsFolderStat.Size(),
  1523  							LastModifiedDate: viewsFolderStat.ModTime(),
  1524  							RemoteAttribute:  filepath.Join("new", "Folder"),
  1525  						},
  1526  					},
  1527  				},
  1528  			},
  1529  			wantRet: IndexerRet{
  1530  				FilesChanged:  []string{jsFileAbsPath, viewsFolderPath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
  1531  				RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder0"), filepath.Join("new", "Folder0", "views.html")},
  1532  				NewFileMap:    normalFileMap,
  1533  			},
  1534  			wantErr: false,
  1535  		},
  1536  		{
  1537  			name: "case 11: remote added for a file but local path and remote destination are same",
  1538  			args: args{
  1539  				directory:         tempDirectoryName,
  1540  				ignoreRules:       []string{},
  1541  				remoteDirectories: map[string]string{htmlRelFilePath: htmlRelFilePath},
  1542  				existingFileIndex: &FileIndex{
  1543  					Files: map[string]FileData{
  1544  						htmlRelFilePath: {
  1545  							Size:             htmlFileStat.Size(),
  1546  							LastModifiedDate: htmlFileStat.ModTime(),
  1547  						},
  1548  					},
  1549  				},
  1550  			},
  1551  			wantRet: IndexerRet{
  1552  				NewFileMap: map[string]FileData{
  1553  					htmlRelFilePath: {
  1554  						Size:             htmlFileStat.Size(),
  1555  						LastModifiedDate: htmlFileStat.ModTime(),
  1556  						RemoteAttribute:  filepath.ToSlash(htmlRelFilePath),
  1557  					},
  1558  				},
  1559  			},
  1560  			wantErr: false,
  1561  		},
  1562  
  1563  		{
  1564  			name: "case 12: ignore a modified file due to ignore rules",
  1565  			args: args{
  1566  				directory:         tempDirectoryName,
  1567  				ignoreRules:       []string{readmeFileName},
  1568  				remoteDirectories: map[string]string{},
  1569  				existingFileIndex: &FileIndex{
  1570  					Files: map[string]FileData{
  1571  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1572  						viewsFolderName:                    normalFileMap[viewsFolderName],
  1573  						jsFileName:                         normalFileMap[jsFileName],
  1574  						specialCharFolderName:              normalFileMap[specialCharFolderName],
  1575  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1576  					},
  1577  				},
  1578  			},
  1579  			wantRet: IndexerRet{
  1580  				NewFileMap: map[string]FileData{
  1581  					htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1582  					viewsFolderName:                    normalFileMap[viewsFolderName],
  1583  					jsFileName:                         normalFileMap[jsFileName],
  1584  					specialCharFolderName:              normalFileMap[specialCharFolderName],
  1585  					fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1586  				},
  1587  			},
  1588  			wantErr: false,
  1589  		},
  1590  		{
  1591  			name: "case 13: ignore a deleted file due to ignore rules",
  1592  			args: args{
  1593  				directory:         tempDirectoryName,
  1594  				ignoreRules:       []string{"blah"},
  1595  				remoteDirectories: map[string]string{},
  1596  				existingFileIndex: &FileIndex{
  1597  					Files: map[string]FileData{
  1598  						readmeFileName:                     normalFileMap[readmeFileName],
  1599  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1600  						viewsFolderName:                    normalFileMap[viewsFolderName],
  1601  						jsFileName:                         normalFileMap[jsFileName],
  1602  						"blah":                             {},
  1603  						specialCharFolderName:              normalFileMap[specialCharFolderName],
  1604  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1605  					},
  1606  				},
  1607  			},
  1608  			wantRet: IndexerRet{
  1609  				NewFileMap: normalFileMap,
  1610  			},
  1611  			wantErr: false,
  1612  		},
  1613  		{
  1614  			name: "case 14: ignore a added file due to ignore rules",
  1615  			args: args{
  1616  				directory:         tempDirectoryName,
  1617  				ignoreRules:       []string{readmeFileName},
  1618  				remoteDirectories: map[string]string{},
  1619  				existingFileIndex: &FileIndex{
  1620  					Files: map[string]FileData{
  1621  						htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1622  						viewsFolderName:                    normalFileMap[viewsFolderName],
  1623  						jsFileName:                         normalFileMap[jsFileName],
  1624  						specialCharFolderName:              normalFileMap[specialCharFolderName],
  1625  						fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1626  					},
  1627  				},
  1628  			},
  1629  			wantRet: IndexerRet{
  1630  				NewFileMap: map[string]FileData{
  1631  					htmlRelFilePath:                    normalFileMap[htmlRelFilePath],
  1632  					viewsFolderName:                    normalFileMap[viewsFolderName],
  1633  					jsFileName:                         normalFileMap[jsFileName],
  1634  					specialCharFolderName:              normalFileMap[specialCharFolderName],
  1635  					fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
  1636  				},
  1637  			},
  1638  			wantErr: false,
  1639  		},
  1640  		{
  1641  			name: "case 15: local file doesn't exist",
  1642  			args: args{
  1643  				directory:         tempDirectoryName,
  1644  				ignoreRules:       []string{},
  1645  				remoteDirectories: map[string]string{htmlRelFilePath + "blah": htmlRelFilePath},
  1646  				existingFileIndex: &FileIndex{
  1647  					Files: map[string]FileData{},
  1648  				},
  1649  			},
  1650  			wantRet: IndexerRet{},
  1651  			wantErr: true,
  1652  		},
  1653  		{
  1654  			name: "case 16: local folder doesn't exist",
  1655  			args: args{
  1656  				directory:         tempDirectoryName,
  1657  				ignoreRules:       []string{},
  1658  				remoteDirectories: map[string]string{viewsFolderPath + "blah": viewsFolderPath},
  1659  				existingFileIndex: &FileIndex{
  1660  					Files: map[string]FileData{},
  1661  				},
  1662  			},
  1663  			wantRet: IndexerRet{},
  1664  			wantErr: true,
  1665  		},
  1666  	}
  1667  	for _, tt := range tests {
  1668  		t.Run(tt.name, func(t *testing.T) {
  1669  			gotRet, err := runIndexerWithExistingFileIndex(tt.args.directory, tt.args.ignoreRules, tt.args.remoteDirectories, tt.args.existingFileIndex)
  1670  			if (err != nil) != tt.wantErr {
  1671  				t.Errorf("runIndexerWithExistingFileIndex() error = %v, wantErr %v", err, tt.wantErr)
  1672  				return
  1673  			}
  1674  
  1675  			if err != nil && tt.wantErr {
  1676  				return
  1677  			}
  1678  
  1679  			sortOpt := cmpopts.SortSlices(func(x, y string) bool {
  1680  				return x < y
  1681  			})
  1682  
  1683  			if diff := cmp.Diff(tt.wantRet.FilesChanged, gotRet.FilesChanged, sortOpt); diff != "" {
  1684  				t.Errorf("runIndexerWithExistingFileIndex() FilesChanged mismatch (-want +got):\n%s", diff)
  1685  			}
  1686  			if diff := cmp.Diff(tt.wantRet.NewFileMap, gotRet.NewFileMap); diff != "" {
  1687  				t.Errorf("runIndexerWithExistingFileIndex() NewFileMap mismatch (-want +got):\n%s", diff)
  1688  			}
  1689  
  1690  			if diff := cmp.Diff(tt.wantRet.FilesDeleted, gotRet.FilesDeleted, sortOpt); diff != "" {
  1691  				t.Errorf("runIndexerWithExistingFileIndex() FilesDeleted mismatch (-want +got):\n%s", diff)
  1692  			}
  1693  			if diff := cmp.Diff(tt.wantRet.RemoteDeleted, gotRet.RemoteDeleted, sortOpt); diff != "" {
  1694  				t.Errorf("runIndexerWithExistingFileIndex() RemoteDeleted mismatch (-want +got):\n%s", diff)
  1695  			}
  1696  		})
  1697  	}
  1698  }
  1699  
  1700  // Copied from: https://go-review.googlesource.com/c/go/+/18034/2/src/path/filepath/match_test.go
  1701  func Test_globEscape(t *testing.T) {
  1702  	cases := []struct {
  1703  		value string
  1704  		want  string
  1705  	}{
  1706  		{"abc           d", "abc           d"},
  1707  		{"*abc           d", "[*]abc           d"},
  1708  		{"*****", "[*][*][*][*][*]"},
  1709  		{"[]*abDEFG?", "[[]][*]abDEFG[?]"},
  1710  		{"a*", "a[*]"},
  1711  		{"a*b*c*d*e*/f", "a[*]b[*]c[*]d[*]e[*]/f"},
  1712  		{"a*b?c*x", "a[*]b[?]c[*]x"},
  1713  		{"ab[c]", "ab[[]c]"},
  1714  		{"ab[b-d]", "ab[[]b-d]"},
  1715  		{"ab[^c]", "ab[[]^c]"},
  1716  		{"ab[^b-d]", "ab[[]^b-d]"},
  1717  		{"a???b", "a[?][?][?]b"},
  1718  		{"a\\\\???b", "a\\\\[?][?][?]b"},
  1719  		{"foo\\[bar]xyzzy", "foo\\[[]bar]xyzzy"},
  1720  		{"(\\*\\?\\[\\])", "(\\[*]\\[?]\\[[]\\])"},
  1721  		{"a\\?\\?\\?b", "a\\[?]\\[?]\\[?]b"},
  1722  		{"a\\\\?\\?\\?b", "a\\\\[?]\\[?]\\[?]b"},
  1723  		{"a[^a][^a][^a]b☺", "a[[]^a][[]^a][[]^a]b☺"},
  1724  		{"[a-ζ]*", "[[]a-ζ][*]"},
  1725  		{"*[a-ζ]*", "[*][[]a-ζ][*]"},
  1726  		{"[\\]a]", "[[]\\]a]"},
  1727  		{"lmnopqrstuva]", "lmnopqrstuva]"},
  1728  		{"こんにちは", "こんにちは"},
  1729  		{"こ[んに]ちは", "こ[[]んに]ちは"},
  1730  	}
  1731  	for _, tc := range cases {
  1732  		if got := globEscape(tc.value); got != tc.want {
  1733  			t.Errorf("GlobEscape: %q expected %q got %q", tc.value, tc.want, got)
  1734  		}
  1735  	}
  1736  }
  1737  

View as plain text