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  }