github.com/jonsyu1/godel@v0.0.0-20171017211503-64567a0cf169/layout/operations_test.go (about)

     1  // Copyright 2016 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package layout_test
    16  
    17  import (
    18  	"io/ioutil"
    19  	"os"
    20  	"path"
    21  	"testing"
    22  
    23  	"github.com/nmiyake/pkg/dirs"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/palantir/godel/layout"
    28  )
    29  
    30  type Spec struct {
    31  	Path    string
    32  	IsDir   bool
    33  	Content string
    34  }
    35  
    36  type Specs []Spec
    37  
    38  func (s Specs) AllPaths() map[string]bool {
    39  	m := make(map[string]bool, len(s))
    40  	for _, c := range s {
    41  		m[c.Path] = c.IsDir
    42  		// if path is a file, populate entries for all parent directories
    43  		if !c.IsDir {
    44  			for d := path.Dir(c.Path); d != "."; d = path.Dir(d) {
    45  				m[d] = true
    46  			}
    47  		}
    48  	}
    49  	return m
    50  }
    51  
    52  func Dir(path string) Spec {
    53  	return Spec{
    54  		Path: path,
    55  	}
    56  }
    57  
    58  func File(path, content string) Spec {
    59  	return Spec{
    60  		Path:    path,
    61  		Content: content,
    62  	}
    63  }
    64  
    65  func TestSyncDir(t *testing.T) {
    66  	tmpDir, cleanup, err := dirs.TempDir("", "")
    67  	defer cleanup()
    68  	require.NoError(t, err)
    69  
    70  	for i, currCase := range []struct {
    71  		srcDirLayout  Specs
    72  		dstDirLayout  Specs
    73  		skip          []string
    74  		wantModified  bool
    75  		wantDirLayout Specs
    76  	}{
    77  		// no modification occurs if src and dst the same
    78  		{
    79  			srcDirLayout: []Spec{
    80  				File("both.txt", "src"),
    81  				Dir("both"),
    82  			},
    83  			dstDirLayout: []Spec{
    84  				File("both.txt", "src"),
    85  				Dir("both"),
    86  			},
    87  			wantModified: false,
    88  		},
    89  		// no modification occurs if paths to sync are skipped
    90  		{
    91  			srcDirLayout: []Spec{
    92  				Dir("src-only"),
    93  			},
    94  			dstDirLayout: []Spec{
    95  				File("dst-only.txt", "dst"),
    96  			},
    97  			skip: []string{
    98  				"src-only",
    99  				"dst-only.txt",
   100  			},
   101  			wantModified: false,
   102  		},
   103  		// sync files
   104  		{
   105  			srcDirLayout: []Spec{
   106  				File("src-only.txt", "src-only"),
   107  				File("both.txt", "src"),
   108  			},
   109  			dstDirLayout: []Spec{
   110  				File("dst-only.txt", "dst-only"),
   111  				File("both.txt", "dst"),
   112  			},
   113  			wantDirLayout: []Spec{
   114  				File("src-only.txt", "src-only"),
   115  				File("both.txt", "src"),
   116  			},
   117  			wantModified: true,
   118  		},
   119  		// sync directories
   120  		{
   121  			srcDirLayout: []Spec{
   122  				Dir("src-only"),
   123  				File("both/both.txt", "src"),
   124  			},
   125  			dstDirLayout: []Spec{
   126  				Dir("dst-only"),
   127  				File("both/both.txt", "dst"),
   128  			},
   129  			wantDirLayout: []Spec{
   130  				Dir("src-only"),
   131  				File("both/both.txt", "src"),
   132  			},
   133  			wantModified: true,
   134  		},
   135  		// sync paths with different types
   136  		{
   137  			srcDirLayout: []Spec{
   138  				Dir("dir-in-src"),
   139  				File("file-in-src", "src"),
   140  			},
   141  			dstDirLayout: []Spec{
   142  				File("dir-in-src", "dst"),
   143  				Dir("file-in-src"),
   144  			},
   145  			wantDirLayout: []Spec{
   146  				Dir("dir-in-src"),
   147  				File("file-in-src", "src"),
   148  			},
   149  			wantModified: true,
   150  		},
   151  		// sync operation with multiple actions
   152  		{
   153  			srcDirLayout: []Spec{
   154  				File("foo.txt", "foo"),
   155  				File("baz.txt", "src-baz"),
   156  				Dir("dir-in-src"),
   157  			},
   158  			dstDirLayout: []Spec{
   159  				File("bar.txt", "bar"),
   160  				File("baz.txt", "dst-baz"),
   161  				File("dir-in-src", "file"),
   162  			},
   163  			wantDirLayout: []Spec{
   164  				File("foo.txt", "foo"),
   165  				File("baz.txt", "src-baz"),
   166  				Dir("dir-in-src"),
   167  			},
   168  			wantModified: true,
   169  		},
   170  	} {
   171  		srcDir, err := ioutil.TempDir(tmpDir, "src")
   172  		require.NoError(t, err, "Case %d", i)
   173  		writeLayout(t, srcDir, currCase.srcDirLayout)
   174  
   175  		dstDir, err := ioutil.TempDir(tmpDir, "dst")
   176  		require.NoError(t, err, "Case %d", i)
   177  		writeLayout(t, dstDir, currCase.dstDirLayout)
   178  
   179  		modified, err := layout.SyncDir(srcDir, dstDir, currCase.skip)
   180  		require.NoError(t, err, "Case %d", i)
   181  
   182  		assert.Equal(t, currCase.wantModified, modified, "Case %d", i)
   183  		if currCase.wantModified {
   184  			// if modification is expected, verify result
   185  			assertLayoutEqual(t, i, currCase.wantDirLayout, dstDir)
   186  		} else {
   187  			// if modification is not expected,
   188  			assertLayoutEqual(t, i, currCase.dstDirLayout, dstDir)
   189  		}
   190  	}
   191  
   192  }
   193  
   194  func assertLayoutEqual(t *testing.T, caseNum int, want Specs, got string) {
   195  	// verify that paths are correct (catches case where path not in spec exists)
   196  	gotPaths, err := layout.AllPaths(got)
   197  	require.NoError(t, err, "Case %d", caseNum)
   198  	assert.Equal(t, want.AllPaths(), gotPaths)
   199  
   200  	// verify that provided directory matches all provided specs
   201  	for _, curr := range want {
   202  		p := path.Join(got, curr.Path)
   203  
   204  		fi, err := os.Stat(p)
   205  		assert.NoError(t, err, "Case %d", caseNum)
   206  		assert.Equal(t, curr.IsDir, fi.IsDir(), "Case %d", caseNum)
   207  
   208  		if !curr.IsDir {
   209  			content, err := ioutil.ReadFile(p)
   210  			require.NoError(t, err, "Case %d", caseNum)
   211  			assert.Equal(t, curr.Content, string(content), "Case %d", caseNum)
   212  		}
   213  	}
   214  }
   215  
   216  func writeLayout(t *testing.T, dir string, specs []Spec) {
   217  	for _, curr := range specs {
   218  		p := path.Join(dir, curr.Path)
   219  
   220  		dir := p
   221  		if !curr.IsDir {
   222  			dir = path.Dir(dir)
   223  		}
   224  		err := os.MkdirAll(dir, 0755)
   225  		require.NoError(t, err, "Failed to create directory %v", dir)
   226  
   227  		if !curr.IsDir {
   228  			err = ioutil.WriteFile(p, []byte(curr.Content), 0644)
   229  			require.NoError(t, err, "Failed to write file %v", p)
   230  		}
   231  	}
   232  }