github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/ln/ln_test.go (about) 1 // Copyright 2016 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // created by Manoel Vilela <manoel_vilela@engineer.com> 6 7 package main 8 9 import ( 10 "os" 11 "path/filepath" 12 "testing" 13 ) 14 15 type create struct { 16 name string 17 dir bool 18 } 19 20 type result struct { 21 symlink bool 22 name, linksTo string 23 } 24 25 type test struct { 26 conf config // conf with flags 27 args []string // to pass for ln 28 results []result // expected results 29 files []create // previous files for testing 30 cmdline string // cmdline ln equivalent 31 } 32 33 // loadTests loads the main table driven tests 34 // for ln command tests 35 func loadTests() []test { 36 return []test{ 37 { 38 // covers usage: 39 // ln [OPTIONS]... [-T] TARGET LINK_NAME (1st form) (posix) 40 config{}, 41 []string{"a", "b"}, 42 []result{{name: "b", linksTo: "a"}}, 43 []create{{name: "a"}}, 44 "ln a b", 45 }, 46 { 47 config{symlink: true}, 48 []string{"a", "b"}, 49 []result{{symlink: true, name: "b", linksTo: "a"}}, 50 []create{{name: "a"}}, 51 "ln -s a b", 52 }, 53 { 54 // covers usage: 55 // ln [OPTIONS]... TARGET (2nd form) (gnu) 56 config{symlink: true}, 57 []string{"bin/cp"}, 58 []result{ 59 {symlink: true, name: "cp", linksTo: "bin/cp"}, 60 }, 61 []create{ 62 {name: "bin", dir: true}, 63 {name: "bin/cp"}, 64 }, 65 "ln -s bin/cp", 66 }, 67 { 68 // covers usage: 69 // ln [OPTIONS]... TARGET... DIRECTORY (3rd form) (posix) 70 config{symlink: true}, 71 []string{"bin/cp", "bin/ls", "bin/ln", "."}, 72 []result{ 73 {symlink: true, name: "cp", linksTo: "bin/cp"}, 74 {symlink: true, name: "ls", linksTo: "bin/ls"}, 75 {symlink: true, name: "ln", linksTo: "bin/ln"}, 76 }, 77 []create{ 78 {name: "bin", dir: true}, 79 {name: "bin/cp"}, 80 {name: "bin/ls"}, 81 {name: "bin/ln"}, 82 }, 83 "ln -s bin/cp bin/ls bin/ln .", 84 }, 85 { 86 // covers usage: 87 // ln [OPTIONS]... -t DIRECTORY TARGET... (4th form) (gnu) 88 config{symlink: true, dirtgt: "."}, 89 []string{"bin/cp", "bin/ls", "bin/ln"}, 90 []result{ 91 {symlink: true, name: "cp", linksTo: "bin/cp"}, 92 {symlink: true, name: "ls", linksTo: "bin/ls"}, 93 {symlink: true, name: "ln", linksTo: "bin/ln"}, 94 }, 95 []create{ 96 {name: "bin", dir: true}, 97 {name: "bin/cp"}, 98 {name: "bin/ls"}, 99 {name: "bin/ln"}, 100 }, 101 "ln -s bin/cp bin/ls bin/ln -t .", 102 }, 103 { 104 // covers usage: 105 // ln [OPTIONS]... -t DIRECTORY TARGET... (4th form) (gnu) 106 config{symlink: true, dirtgt: "folder", relative: true}, 107 []string{"cp", "ls", "ln"}, 108 []result{ 109 {symlink: true, name: "folder/cp", linksTo: "../cp"}, 110 {symlink: true, name: "folder/ls", linksTo: "../ls"}, 111 {symlink: true, name: "folder/ln", linksTo: "../ln"}, 112 }, 113 []create{ 114 {name: "folder", dir: true}, 115 {name: "cp"}, 116 {name: "ls"}, 117 {name: "ln"}, 118 }, 119 "ln -s -v -r -t folder cp ls ln", 120 }, 121 { 122 // -i -f mutually exclusive (f overwrite evers) 123 config{force: true, prompt: true}, 124 []string{"a", "overwrite"}, 125 []result{ 126 {name: "overwrite", linksTo: "a"}, 127 }, 128 []create{ 129 {name: "overwrite"}, 130 {name: "a"}, 131 }, 132 "ln -i -f a overwrite", 133 }, 134 } 135 } 136 137 // testHardLink test if hardlink creation was successful 138 // 'target' and 'linkName' must exists 139 // linkName -> target 140 func testHardLink(linkName, target string, t *testing.T) { 141 linkStat, err := os.Stat(linkName) 142 if err != nil { 143 t.Errorf("stat %q failed: %v", linkName, err) 144 } 145 targetStat, err := os.Stat(target) 146 if err != nil { 147 t.Errorf("stat %q failed: %v", target, err) 148 } 149 if !os.SameFile(linkStat, targetStat) { 150 t.Errorf("link %q, %q did not create hard link", linkName, target) 151 } 152 } 153 154 // testSymllink test if symlink creation was successful 155 // 'target' and 'linkName' must exists 156 // linkName -> target 157 func testSymlink(linkName, linksTo string, t *testing.T) { 158 target := linksTo 159 if !filepath.IsAbs(target) { 160 target = filepath.Base(target) 161 } 162 163 linkStat, err := os.Stat(linkName) 164 if err != nil { 165 t.Errorf("stat %q failed: %v", linkName, err) 166 } 167 targetStat, err := os.Stat(target) 168 if err != nil { 169 t.Errorf("stat %q failed: %v", target, err) 170 } 171 if !os.SameFile(linkStat, targetStat) { 172 t.Errorf("symlink %q, %q did not create symlink", linkName, target) 173 } 174 targetStat, err = os.Stat(target) 175 if err != nil { 176 t.Errorf("lstat %q failed: %v", target, err) 177 } 178 179 if targetStat.Mode()&os.ModeSymlink == os.ModeSymlink { 180 t.Errorf("symlink %q, %q did not create symlink", linkName, target) 181 } 182 183 targetStat, err = os.Stat(target) 184 if err != nil { 185 t.Errorf("stat %q failed: %v", target, err) 186 } 187 if targetStat.Mode()&os.ModeSymlink != 0 { 188 t.Errorf("stat %q did not follow symlink", target) 189 } 190 s, err := os.Readlink(linkName) 191 if err != nil { 192 t.Errorf("readlink %q failed: %v", target, err) 193 } 194 if s != linksTo { 195 t.Errorf("after symlink %q != %q", s, target) 196 } 197 file, err := os.Open(target) 198 if err != nil { 199 t.Errorf("open %q failed: %v", target, err) 200 } 201 file.Close() 202 } 203 204 // TestLn make a general tests based on 205 // tabDriven tests (see loadTests()) 206 func TestLn(t *testing.T) { 207 tabDriven := loadTests() 208 testDir := t.TempDir() 209 210 // executing ln on isolated testDir 211 if err := os.Chdir(testDir); err != nil { 212 t.Fatalf("Changing directory for %q fails: %v", testDir, err) 213 } 214 defer os.Chdir("..") // after defer to go back to the original root 215 216 for caseNum, testCase := range tabDriven { 217 d := t.TempDir() 218 if err := os.Chdir(d); err != nil { 219 t.Fatalf("Changing directory for %q fails: %v", d, err) 220 } 221 222 for _, f := range testCase.files { 223 t.Logf("Creating: %v (dir: %v)", f.name, f.dir) 224 p := filepath.Join(f.name) 225 if f.dir { 226 if err := os.Mkdir(p, 0o750); err != nil && err == os.ErrExist { 227 t.Skipf("Creation of dir %q fails: %v", p, err) 228 } 229 } else { 230 if err := os.WriteFile(p, []byte{'.'}, 0o640); err != nil { 231 t.Fatal(err) 232 } 233 } 234 235 } 236 237 if err := testCase.conf.ln(testCase.args); err != nil { 238 t.Errorf("Fails: test [%d]. %v", caseNum+1, err) 239 continue 240 } 241 242 t.Logf("Testing cmdline: %q", testCase.cmdline) 243 for _, expected := range testCase.results { 244 if expected.symlink { 245 t.Logf("%q -> %q (symlink)", expected.name, expected.linksTo) 246 testSymlink(expected.name, expected.linksTo, t) 247 } else { 248 t.Logf("%q -> %q (hardlink)", expected.name, expected.linksTo) 249 testHardLink(expected.name, expected.linksTo, t) 250 } 251 } 252 253 // backing to testDir folder 254 os.Chdir("..") 255 } 256 }