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 }