blob: 4866f20e3e29a1fc2016c55df1f108797d0daa4d [file] [log] [blame]
// Copyright 2019 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kubernetes
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"testing"
"cuelang.org/go/internal/cuetest"
"github.com/kylelemons/godebug/diff"
"github.com/retr0h/go-gilt/copy"
)
var (
update = flag.Bool("update", false, "update test data")
cleanup = flag.Bool("cleanup", true, "clean up generated files")
)
func TestTutorial(t *testing.T) {
if testing.Short() {
t.Skip()
}
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
// Read the tutorial.
b, err := ioutil.ReadFile("README.md")
if err != nil {
t.Fatal(err)
}
// Copy test data and change the cwd to this directory.
dir, err := ioutil.TempDir("", "tutorial")
if err != nil {
log.Fatal(err)
}
if *cleanup {
defer os.RemoveAll(dir)
} else {
defer logf(t, "Temporary dir: %v", dir)
}
wd := filepath.Join(dir, "services")
if err := copy.Dir(filepath.Join("original", "services"), wd); err != nil {
t.Fatal(err)
}
if *update {
// The test environment won't work in all environments. We create
// a fake go.mod so that Go will find the module root. By default
// we won't set it.
if err := os.Chdir(dir); err != nil {
t.Fatal(err)
}
cmd := exec.Command("go", "mod", "init", "cuelang.org/dummy")
b, err := cmd.CombinedOutput()
logf(t, string(b))
if err != nil {
t.Fatal(err)
}
} else {
// We only fetch new kubernetes files with when updating.
err := copy.Dir(filepath.Join("quick", "pkg"), filepath.Join(dir, "pkg"))
if err != nil {
t.Fatal(err)
}
}
if err := os.Chdir(wd); err != nil {
t.Fatal(err)
}
defer os.Chdir(cwd)
logf(t, "Changed to directory: %s", wd)
// Execute the tutorial.
for c := cuetest.NewChunker(t, b); c.Next("```", "```"); {
for c := cuetest.NewChunker(t, c.Bytes()); c.Next("$ ", "\n"); {
alt := c.Text()
cmd := strings.Replace(alt, "<<EOF", "", -1)
input := ""
if cmd != alt {
if !c.Next("", "EOF") {
t.Fatalf("non-terminated <<EOF")
}
input = c.Text()
}
redirect := ""
if p := strings.Index(cmd, " >"); p > 0 {
redirect = cmd[p+1:]
cmd = cmd[:p]
}
switch cmd = strings.TrimSpace(cmd); {
case strings.HasPrefix(cmd, "cat"):
if input == "" {
break
}
var r *os.File
var err error
if strings.HasPrefix(redirect, ">>") {
// Append input
r, err = os.OpenFile(
strings.TrimSpace(redirect[2:]),
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
0666)
} else { // strings.HasPrefix(redirect, ">")
// Create new file with input
r, err = os.Create(strings.TrimSpace(redirect[1:]))
}
if err != nil {
t.Fatal(err)
}
_, err = io.WriteString(r, input)
if err := r.Close(); err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatal(err)
}
case strings.HasPrefix(cmd, "cue "):
logf(t, "$ %s", cmd)
if strings.HasPrefix(cmd, "cue create") {
// Don't execute the kubernetes dry run.
break
}
if !*update && strings.HasPrefix(cmd, "cue get") {
// Don't fetch stuff in normal mode.
break
}
cuetest.Run(t, wd, cmd, &cuetest.Config{
Stdin: strings.NewReader(input),
Stdout: ioutil.Discard,
})
case strings.HasPrefix(cmd, "sed "),
strings.HasPrefix(cmd, "touch "):
logf(t, "$ %s", cmd)
args := cuetest.SplitArgs(t, cmd)
cx := exec.Command(args[0], args[1:]...)
if input != "" {
cx.Stdin = strings.NewReader(input)
cx.Stdout = ioutil.Discard
}
if err := cx.Run(); err != nil {
t.Fatal(err)
}
}
}
}
if err := os.Chdir(filepath.Join(cwd, "quick")); err != nil {
t.Fatal(err)
}
if *update {
// Remove all old cue files.
filepath.Walk("", func(path string, info os.FileInfo, err error) error {
if isCUE(path) {
if err := os.Remove(path); err != nil {
t.Fatal(err)
}
}
return nil
})
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if isCUE(path) {
dst := path[len(dir)+1:]
err := os.MkdirAll(filepath.Dir(dst), 0755)
if err != nil {
return err
}
return copy.File(path, dst)
}
return nil
})
if err != nil {
t.Fatal(err)
}
return
}
// Compare the output in the temp directory with the quick output.
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if filepath.Ext(path) != ".cue" {
return nil
}
b1, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
b2, err := ioutil.ReadFile(path[len(dir)+1:])
if err != nil {
t.Fatal(err)
}
got, want := string(b1), string(b2)
if got != want {
t.Log(diff.Diff(got, want))
return fmt.Errorf("file %q differs", path)
}
return nil
})
if err != nil {
t.Error(err)
}
}
func isCUE(filename string) bool {
return filepath.Ext(filename) == ".cue" && !strings.Contains(filename, "_tool")
}
func logf(t *testing.T, format string, args ...interface{}) {
t.Logf(format, args...)
log.Printf(format, args...)
}
// TODO:
// Test manual and quick: evaluation results in output of testdata directory.
func TestEval(t *testing.T) {
for _, dir := range []string{"quick", "manual"} {
t.Run(dir, func(t *testing.T) {
buf := &bytes.Buffer{}
cuetest.Run(t, dir, "cue eval ./...", &cuetest.Config{
Stdout: buf,
})
cwd, _ := os.Getwd()
pattern := fmt.Sprintf("//.*%s.*", regexp.QuoteMeta(filepath.Join(cwd, dir)))
re, err := regexp.Compile(pattern)
if err != nil {
t.Fatal(err)
}
got := re.ReplaceAll(buf.Bytes(), []byte{})
got = bytes.TrimSpace(got)
testfile := filepath.Join("testdata", dir+".out")
if *update {
err := ioutil.WriteFile(testfile, got, 0644)
if err != nil {
t.Fatal(err)
}
return
}
b, err := ioutil.ReadFile(testfile)
if err != nil {
t.Fatal(err)
}
if got, want := string(got), string(b); got != want {
t.Errorf("files differ:\n%s", diff.Diff(got, want))
}
})
}
}