1// Copyright 2020 The 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 15package cmd 16 17import ( 18 "bufio" 19 "bytes" 20 "context" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path" 25 "path/filepath" 26 goruntime "runtime" 27 "strings" 28 "testing" 29 30 "github.com/rogpeppe/go-internal/goproxytest" 31 "github.com/rogpeppe/go-internal/gotooltest" 32 "github.com/rogpeppe/go-internal/testscript" 33 "github.com/rogpeppe/go-internal/txtar" 34 35 "cuelang.org/go/cue/errors" 36 "cuelang.org/go/cue/parser" 37 "cuelang.org/go/internal/cuetest" 38) 39 40const ( 41 homeDirName = ".user-home" 42) 43 44// TestLatest checks that the examples match the latest language standard, 45// even if still valid in backwards compatibility mode. 46func TestLatest(t *testing.T) { 47 root := filepath.Join("testdata", "script") 48 filepath.Walk(root, func(fullpath string, info os.FileInfo, err error) error { 49 if !strings.HasSuffix(fullpath, ".txt") || 50 strings.HasPrefix(filepath.Base(fullpath), "fix") { 51 return nil 52 } 53 54 a, err := txtar.ParseFile(fullpath) 55 if err != nil { 56 t.Error(err) 57 return nil 58 } 59 if bytes.HasPrefix(a.Comment, []byte("!")) { 60 return nil 61 } 62 63 for _, f := range a.Files { 64 t.Run(path.Join(fullpath, f.Name), func(t *testing.T) { 65 if !strings.HasSuffix(f.Name, ".cue") { 66 return 67 } 68 v := parser.FromVersion(parser.Latest) 69 _, err := parser.ParseFile(f.Name, f.Data, v) 70 if err != nil { 71 w := &bytes.Buffer{} 72 fmt.Fprintf(w, "\n%s:\n", fullpath) 73 errors.Print(w, err, nil) 74 t.Error(w.String()) 75 } 76 }) 77 } 78 return nil 79 }) 80} 81 82func TestScript(t *testing.T) { 83 srv, err := goproxytest.NewServer(filepath.Join("testdata", "mod"), "") 84 if err != nil { 85 t.Fatalf("cannot start proxy: %v", err) 86 } 87 p := testscript.Params{ 88 Dir: filepath.Join("testdata", "script"), 89 UpdateScripts: cuetest.UpdateGoldenFiles, 90 Setup: func(e *testscript.Env) error { 91 // Set up a home dir within work dir with a . prefix so that the 92 // Go/CUE pattern ./... does not descend into it. 93 home := filepath.Join(e.WorkDir, homeDirName) 94 if err := os.Mkdir(home, 0777); err != nil { 95 return err 96 } 97 98 e.Vars = append(e.Vars, 99 "GOPROXY="+srv.URL, 100 "GONOSUMDB=*", // GOPROXY is a private proxy 101 homeEnvName()+"="+home, 102 ) 103 return nil 104 }, 105 Condition: cuetest.Condition, 106 } 107 if err := gotooltest.Setup(&p); err != nil { 108 t.Fatal(err) 109 } 110 testscript.Run(t, p) 111} 112 113// TestScriptDebug takes a single testscript file and then runs it within the 114// same process so it can be used for debugging. It runs the first cue command 115// it finds. 116// 117// Usage Comment out t.Skip() and set file to test. 118func TestX(t *testing.T) { 119 t.Skip() 120 const path = "./testdata/script/eval_e.txt" 121 122 check := func(err error) { 123 t.Helper() 124 if err != nil { 125 t.Fatal(err) 126 } 127 } 128 129 tmpdir, err := ioutil.TempDir("", "cue-script") 130 check(err) 131 defer os.Remove(tmpdir) 132 133 a, err := txtar.ParseFile(filepath.FromSlash(path)) 134 check(err) 135 136 for _, f := range a.Files { 137 name := filepath.Join(tmpdir, f.Name) 138 check(os.MkdirAll(filepath.Dir(name), 0777)) 139 check(ioutil.WriteFile(name, f.Data, 0666)) 140 } 141 142 cwd, err := os.Getwd() 143 check(err) 144 defer func() { _ = os.Chdir(cwd) }() 145 _ = os.Chdir(tmpdir) 146 147 for s := bufio.NewScanner(bytes.NewReader(a.Comment)); s.Scan(); { 148 cmd := s.Text() 149 cmd = strings.TrimLeft(cmd, "! ") 150 if strings.HasPrefix(cmd, "exec ") { 151 cmd = cmd[len("exec "):] 152 } 153 if !strings.HasPrefix(cmd, "cue ") { 154 continue 155 } 156 157 // TODO: Ugly hack to get args. Do more principled parsing. 158 var args []string 159 for _, a := range strings.Split(cmd, " ")[1:] { 160 args = append(args, strings.Trim(a, "'")) 161 } 162 163 c, err := New(args) 164 check(err) 165 b := &bytes.Buffer{} 166 c.SetOutput(b) 167 err = c.Run(context.Background()) 168 // Always create an error to show 169 t.Error(err, "\n", b.String()) 170 return 171 } 172 t.Fatal("NO COMMAND FOUND") 173} 174 175func TestMain(m *testing.M) { 176 // Setting inTest causes filenames printed in error messages 177 // to be normalized so the output looks the same on Unix 178 // as Windows. 179 os.Exit(testscript.RunMain(m, map[string]func() int{ 180 "cue": MainTest, 181 })) 182} 183 184// homeEnvName extracts the logic from os.UserHomeDir to get the 185// name of the environment variable that should be used when 186// seting the user's home directory 187func homeEnvName() string { 188 switch goruntime.GOOS { 189 case "windows": 190 return "USERPROFILE" 191 case "plan9": 192 return "home" 193 default: 194 return "HOME" 195 } 196} 197