cuelang.org/go@v0.10.1/pkg/tool/file/file_test.go (about)

     1  // Copyright 2019 CUE Authors
     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 file
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"reflect"
    22  	"runtime"
    23  	"testing"
    24  
    25  	"cuelang.org/go/cue"
    26  	"cuelang.org/go/cue/parser"
    27  	"cuelang.org/go/internal/task"
    28  	"cuelang.org/go/internal/value"
    29  	"cuelang.org/go/pkg/internal"
    30  	"github.com/go-quicktest/qt"
    31  )
    32  
    33  func parse(t *testing.T, kind, expr string) cue.Value {
    34  	t.Helper()
    35  
    36  	x, err := parser.ParseExpr("test", expr)
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	v := internal.NewContext().BuildExpr(x)
    41  	if err := v.Err(); err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	return value.UnifyBuiltin(v, kind)
    45  }
    46  
    47  func TestRead(t *testing.T) {
    48  	v := parse(t, "tool/file.Read", `{filename: "testdata/input.foo"}`)
    49  	got, err := (*cmdRead).Run(nil, &task.Context{Obj: v})
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	want := map[string]interface{}{"contents": []byte("This is a test.")}
    54  	if !reflect.DeepEqual(got, want) {
    55  		t.Errorf("got %v; want %v", got, want)
    56  	}
    57  
    58  	v = parse(t, "tool/file.Read", `{
    59  		filename: "testdata/input.foo"
    60  		contents: string
    61  	}`)
    62  	got, err = (*cmdRead).Run(nil, &task.Context{Obj: v})
    63  	if err != nil {
    64  		t.Fatal(err)
    65  	}
    66  	want = map[string]interface{}{"contents": "This is a test."}
    67  	if !reflect.DeepEqual(got, want) {
    68  		t.Errorf("got %v; want %v", got, want)
    69  	}
    70  }
    71  
    72  func TestAppend(t *testing.T) {
    73  	name := filepath.Join(t.TempDir(), "file")
    74  	name = filepath.ToSlash(name)
    75  
    76  	v := parse(t, "tool/file.Append", fmt.Sprintf(`{
    77  		filename: "%s"
    78  		contents: "This is a test."
    79  	}`, name))
    80  	_, err := (*cmdAppend).Run(nil, &task.Context{Obj: v})
    81  	if err != nil {
    82  		t.Fatal(err)
    83  	}
    84  
    85  	b, err := os.ReadFile(name)
    86  	if err != nil {
    87  		t.Fatal(err)
    88  	}
    89  
    90  	if got, want := string(b), "This is a test."; got != want {
    91  		t.Errorf("got %v; want %v", got, want)
    92  	}
    93  }
    94  
    95  func TestCreate(t *testing.T) {
    96  	name := filepath.Join(t.TempDir(), "file")
    97  	name = filepath.ToSlash(name)
    98  
    99  	v := parse(t, "tool/file.Create", fmt.Sprintf(`{
   100  		filename: "%s"
   101  		contents: "This is a test."
   102  	}`, name))
   103  	_, err := (*cmdCreate).Run(nil, &task.Context{Obj: v})
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	b, err := os.ReadFile(name)
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	if got, want := string(b), "This is a test."; got != want {
   114  		t.Errorf("got %v; want %v", got, want)
   115  	}
   116  }
   117  
   118  func TestGlob(t *testing.T) {
   119  	// Simple globbing against testdata.
   120  	v := parse(t, "tool/file.Glob", `{
   121  		glob: "testdata/input.*"
   122  	}`)
   123  	got, err := (*cmdGlob).Run(nil, &task.Context{Obj: v})
   124  	qt.Assert(t, qt.IsNil(err))
   125  	qt.Assert(t, qt.DeepEquals(got, any(map[string]any{"files": []string{"testdata/input.foo"}})))
   126  
   127  	// globstar or recursive globbing is not supported.
   128  	v = parse(t, "tool/file.Glob", `{
   129  		glob: "testdata/**/glob.leaf"
   130  	}`)
   131  	got, err = (*cmdGlob).Run(nil, &task.Context{Obj: v})
   132  	qt.Assert(t, qt.IsNotNil(err))
   133  	qt.Assert(t, qt.IsNil(got))
   134  }
   135  
   136  func TestGlobEscapeStar(t *testing.T) {
   137  	// `\**` is disallowed in a pattern on Windows, as the backslash is a path separator,
   138  	// hence `**` is treated as a globstar which is not yet supported.
   139  	// `\**` is allowed on other OSes as the first star is escaped, and only the second
   140  	// is treated as a wildcard. Thus such a pattern should match a file like `*.test`.
   141  	dir := t.TempDir()
   142  	leafFile := filepath.Join(dir, "*.test")
   143  	if runtime.GOOS != "windows" {
   144  		err := os.WriteFile(leafFile, nil, 0o666)
   145  		qt.Assert(t, qt.IsNil(err))
   146  	}
   147  
   148  	v := parse(t, "tool/file.Glob", `{
   149  		glob: "`+filepath.ToSlash(dir)+`/\\**"
   150  	}`)
   151  	got, err := (*cmdGlob).Run(nil, &task.Context{Obj: v})
   152  	if runtime.GOOS == "windows" {
   153  		qt.Assert(t, qt.IsNotNil(err))
   154  		qt.Assert(t, qt.Equals(got, nil))
   155  	} else {
   156  		qt.Assert(t, qt.IsNil(err))
   157  		qt.Assert(t, qt.DeepEquals(got, any(map[string]any{"files": []string{leafFile}})))
   158  	}
   159  }
   160  
   161  func TestMkdir(t *testing.T) {
   162  	baseDir := t.TempDir()
   163  
   164  	// simple dir creation
   165  	d1 := filepath.Join(baseDir, "foo")
   166  	v := parse(t, "tool/file.Mkdir", fmt.Sprintf(`{path: #"%s"#}`, d1))
   167  	_, err := (*cmdMkdir).Run(nil, &task.Context{Obj: v})
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	fi1, err := os.Stat(d1)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	if !fi1.IsDir() {
   176  		t.Fatal("not a directory")
   177  	}
   178  
   179  	// dir already exists
   180  	v = parse(t, "tool/file.Mkdir", fmt.Sprintf(`{path: #"%s"#}`, d1))
   181  	_, err = (*cmdMkdir).Run(nil, &task.Context{Obj: v})
   182  	if err != nil {
   183  		t.Fatal(err)
   184  	}
   185  
   186  	// create parents
   187  	// set permissions
   188  	d2 := filepath.Join(baseDir, "bar/x")
   189  	v = parse(t, "tool/file.MkdirAll", fmt.Sprintf(`{path: #"%s"#, permissions: 0o700}`, d2))
   190  	_, err = (*cmdMkdir).Run(nil, &task.Context{Obj: v})
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	fi2, err := os.Stat(d2)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	if !fi2.IsDir() {
   199  		t.Fatal("not a directory")
   200  	}
   201  
   202  	// file at same path
   203  	f, err := os.CreateTemp(baseDir, "")
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  	f.Close()
   208  	v = parse(t, "tool/file.Mkdir", fmt.Sprintf(`{path: #"%s"#}`, f.Name()))
   209  	_, err = (*cmdMkdir).Run(nil, &task.Context{Obj: v})
   210  	if err == nil {
   211  		t.Fatal("should not create directory at existing filepath")
   212  	}
   213  }
   214  
   215  func TestMkdirTemp(t *testing.T) {
   216  	// create temp dir
   217  	v := parse(t, "tool/file.MkdirTemp", "{}")
   218  	r, err := (*cmdMkdirTemp).Run(nil, &task.Context{Obj: v})
   219  	if err != nil {
   220  		t.Fatal(err)
   221  	}
   222  	if _, exists := r.(map[string]interface{})["path"]; !exists {
   223  		t.Fatal("no directory path returned")
   224  	}
   225  	path := r.(map[string]interface{})["path"].(string)
   226  	t.Cleanup(func() { os.RemoveAll(path) })
   227  	fi, err := os.Stat(path)
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	if !fi.IsDir() {
   232  		t.Fatal("not a directory")
   233  	}
   234  
   235  	// removes temp dir
   236  	v2 := parse(t, "tool/file.RemoveAll", fmt.Sprintf(`{path: #"%s"#}`, path))
   237  	_, err = (*cmdRemoveAll).Run(nil, &task.Context{Obj: v2})
   238  	if err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	_, err = os.Stat(path)
   242  	if err == nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  }