summaryrefslogtreecommitdiffstats
path: root/main_test.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-01-04 18:24:36 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-05-16 18:01:29 +0200
commit241b21b0fd34d91fccb2ce69874110dceae6f926 (patch)
treed4e0118eac7e9c42f065815447a70805f8d6ad3e /main_test.go
parent6aededf6b42011c3039f5f66487a89a8dd65e0e7 (diff)
Create a struct with all of Hugo's config options
Primary motivation is documentation, but it will also hopefully simplify the code. Also, * Lower case the default output format names; this is in line with the custom ones (map keys) and how it's treated all the places. This avoids doing `stringds.EqualFold` everywhere. Closes #10896 Closes #10620
Diffstat (limited to 'main_test.go')
-rw-r--r--main_test.go382
1 files changed, 382 insertions, 0 deletions
diff --git a/main_test.go b/main_test.go
new file mode 100644
index 000000000..ffd700d57
--- /dev/null
+++ b/main_test.go
@@ -0,0 +1,382 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+//
+// 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 main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/fs"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/bep/helpers/envhelpers"
+ "github.com/gohugoio/hugo/commands"
+ "github.com/rogpeppe/go-internal/testscript"
+)
+
+func TestCommands(t *testing.T) {
+ p := commonTestScriptsParam
+ p.Dir = "testscripts/commands"
+ testscript.Run(t, p)
+}
+
+// Tests in development can be put in "testscripts/unfinished".
+// Also see the watch_testscripts.sh script.
+func TestUnfinished(t *testing.T) {
+ if os.Getenv("CI") != "" {
+ t.Skip("skip unfinished tests on CI")
+ }
+
+ p := commonTestScriptsParam
+ p.Dir = "testscripts/unfinished"
+
+ testscript.Run(t, p)
+}
+
+func TestMain(m *testing.M) {
+ type testInfo struct {
+ BaseURLs []string
+ }
+ os.Exit(
+ testscript.RunMain(m, map[string]func() int{
+ // The main program.
+ "hugo": func() int {
+ err := commands.Execute(os.Args[1:])
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ return 1
+ }
+ return 0
+ },
+ }),
+ )
+}
+
+var commonTestScriptsParam = testscript.Params{
+ Setup: func(env *testscript.Env) error {
+ return testSetupFunc()(env)
+ },
+ Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
+ // log prints to stderr.
+ "log": func(ts *testscript.TestScript, neg bool, args []string) {
+ log.Println(args)
+ },
+ // dostounix converts \r\n to \n.
+ "dostounix": func(ts *testscript.TestScript, neg bool, args []string) {
+ filename := ts.MkAbs(args[0])
+ b, err := os.ReadFile(filename)
+ if err != nil {
+ ts.Fatalf("%v", err)
+ }
+ b = bytes.Replace(b, []byte("\r\n"), []byte{'\n'}, -1)
+ if err := os.WriteFile(filename, b, 0666); err != nil {
+ ts.Fatalf("%v", err)
+ }
+ },
+ // cat prints a file to stdout.
+ "cat": func(ts *testscript.TestScript, neg bool, args []string) {
+ filename := ts.MkAbs(args[0])
+ b, err := os.ReadFile(filename)
+ if err != nil {
+ ts.Fatalf("%v", err)
+ }
+ fmt.Print(string(b))
+ },
+ // sleep sleeps for a second.
+ "sleep": func(ts *testscript.TestScript, neg bool, args []string) {
+ i := 1
+ if len(args) > 0 {
+ var err error
+ i, err = strconv.Atoi(args[0])
+ if err != nil {
+ i = 1
+ }
+ }
+ time.Sleep(time.Duration(i) * time.Second)
+
+ },
+ // ls lists a directory to stdout.
+ "ls": func(ts *testscript.TestScript, neg bool, args []string) {
+ var dirname string
+ if len(args) > 0 {
+ dirname = args[0]
+ }
+ dirname = ts.MkAbs(args[0])
+
+ dir, err := os.Open(dirname)
+ if err != nil {
+ ts.Fatalf("%v", err)
+ }
+ fis, err := dir.Readdir(-1)
+ if err != nil {
+ ts.Fatalf("%v", err)
+ }
+ for _, fi := range fis {
+ fmt.Fprintf(ts.Stdout(), "%s %04o %s %s\n", fi.Mode(), fi.Mode().Perm(), fi.ModTime().Format(time.RFC3339Nano), fi.Name())
+ }
+ },
+ // append appends to a file with a leaading newline.
+ "append": func(ts *testscript.TestScript, neg bool, args []string) {
+ if len(args) < 2 {
+ ts.Fatalf("usage: append FILE TEXT")
+ }
+
+ filename := ts.MkAbs(args[0])
+ words := args[1:]
+ for i, word := range words {
+ words[i] = strings.Trim(word, "\"")
+ }
+ text := strings.Join(words, " ")
+
+ _, err := os.Stat(filename)
+ if err != nil {
+ if os.IsNotExist(err) {
+ ts.Fatalf("file does not exist: %s", filename)
+ }
+ ts.Fatalf("failed to stat file: %v", err)
+ }
+
+ f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o644)
+ if err != nil {
+ ts.Fatalf("failed to open file: %v", err)
+ }
+ defer f.Close()
+
+ _, err = f.WriteString("\n" + text)
+ if err != nil {
+ ts.Fatalf("failed to write to file: %v", err)
+ }
+ },
+ // replace replaces a string in a file.
+ "replace": func(ts *testscript.TestScript, neg bool, args []string) {
+ if len(args) < 3 {
+ ts.Fatalf("usage: replace FILE OLD NEW")
+ }
+ filename := ts.MkAbs(args[0])
+ oldContent, err := os.ReadFile(filename)
+ if err != nil {
+ ts.Fatalf("failed to read file %v", err)
+ }
+ newContent := bytes.Replace(oldContent, []byte(args[1]), []byte(args[2]), -1)
+ err = os.WriteFile(filename, newContent, 0o644)
+ if err != nil {
+ ts.Fatalf("failed to write file: %v", err)
+ }
+ },
+
+ // httpget checks that a HTTP resource's body matches (if it compiles as a regexp) or contains all of the strings given as arguments.
+ "httpget": func(ts *testscript.TestScript, neg bool, args []string) {
+ if len(args) < 2 {
+ ts.Fatalf("usage: httpgrep URL STRING...")
+ }
+
+ tryget := func() error {
+ resp, err := http.Get(args[0])
+ if err != nil {
+ return fmt.Errorf("failed to get URL %q: %v", args[0], err)
+ }
+
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("failed to read response body: %v", err)
+ }
+ for _, s := range args[1:] {
+ re, err := regexp.Compile(s)
+ if err == nil {
+ ok := re.Match(body)
+ if ok != !neg {
+ return fmt.Errorf("response body %q for URL %q does not match %q", body, args[0], s)
+ }
+ } else {
+ ok := bytes.Contains(body, []byte(s))
+ if ok != !neg {
+ return fmt.Errorf("response body %q for URL %q does not contain %q", body, args[0], s)
+ }
+ }
+ }
+ return nil
+
+ }
+
+ // The timing on server rebuilds can be a little tricky to get right,
+ // so we try again a few times until the server is ready.
+ // There may be smarter ways to do this, but this works.
+ start := time.Now()
+ for {
+ time.Sleep(200 * time.Millisecond)
+ err := tryget()
+ if err == nil {
+ return
+ }
+ if time.Since(start) > 6*time.Second {
+ ts.Fatalf("timeout waiting for %q: %v", args[0], err)
+ }
+ }
+ },
+ // checkfile checks that a file exists and is not empty.
+ "checkfile": func(ts *testscript.TestScript, neg bool, args []string) {
+ var readonly, exec bool
+ loop:
+ for len(args) > 0 {
+ switch args[0] {
+ case "-readonly":
+ readonly = true
+ args = args[1:]
+ case "-exec":
+ exec = true
+ args = args[1:]
+ default:
+ break loop
+ }
+ }
+ if len(args) == 0 {
+ ts.Fatalf("usage: checkfile [-readonly] [-exec] file...")
+ }
+
+ for _, filename := range args {
+ filename = ts.MkAbs(filename)
+ fi, err := os.Stat(filename)
+ ok := err == nil != neg
+ if !ok {
+ ts.Fatalf("stat %s: %v", filename, err)
+ }
+ if fi.Size() == 0 {
+ ts.Fatalf("%s is empty", filename)
+ }
+ if readonly && fi.Mode()&0o222 != 0 {
+ ts.Fatalf("%s is writable", filename)
+ }
+ if exec && runtime.GOOS != "windows" && fi.Mode()&0o111 == 0 {
+ ts.Fatalf("%s is not executable", filename)
+ }
+ }
+ },
+
+ // checkfilecount checks that the number of files in a directory is equal to the given count.
+ "checkfilecount": func(ts *testscript.TestScript, neg bool, args []string) {
+ if len(args) != 2 {
+ ts.Fatalf("usage: checkfilecount count dir")
+ }
+ count, err := strconv.Atoi(args[0])
+ if err != nil {
+ ts.Fatalf("invalid count: %v", err)
+ }
+ if count < 0 {
+ ts.Fatalf("count must be non-negative")
+ }
+ dir := args[1]
+ dir = ts.MkAbs(dir)
+
+ found := 0
+
+ filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+ found++
+ return nil
+ })
+
+ ok := found == count != neg
+ if !ok {
+ ts.Fatalf("found %d files, want %d", found, count)
+ }
+ },
+ // waitServer waits for the .ready file to be created by the server.
+ "waitServer": func(ts *testscript.TestScript, neg bool, args []string) {
+ type testInfo struct {
+ BaseURLs []string
+ }
+
+ // The server will write a .ready file when ready.
+ // We wait for that.
+ readyFilename := ts.MkAbs(".ready")
+ limit := time.Now().Add(5 * time.Second)
+ for {
+ _, err := os.Stat(readyFilename)
+ if err != nil {
+ time.Sleep(500 * time.Millisecond)
+ if time.Now().After(limit) {
+ ts.Fatalf("timeout waiting for .ready file")
+ }
+ continue
+ }
+ var info testInfo
+ // Read the .ready file's JSON into info.
+ f, err := os.Open(readyFilename)
+ if err == nil {
+ err = json.NewDecoder(f).Decode(&info)
+ f.Close()
+ } else {
+ ts.Fatalf("failed to open .ready file: %v", err)
+ }
+
+ for i, s := range info.BaseURLs {
+ ts.Setenv(fmt.Sprintf("HUGOTEST_BASEURL_%d", i), s)
+ }
+
+ return
+ }
+
+ },
+ "stopServer": func(ts *testscript.TestScript, neg bool, args []string) {
+ baseURL := ts.Getenv("HUGOTEST_BASEURL_0")
+ if baseURL == "" {
+ ts.Fatalf("HUGOTEST_BASEURL_0 not set")
+ }
+ if !strings.HasSuffix(baseURL, "/") {
+ baseURL += "/"
+ }
+ resp, err := http.Head(baseURL + "__stop")
+ if err != nil {
+ ts.Fatalf("failed to shutdown server: %v", err)
+ }
+ resp.Body.Close()
+ // Allow some time for the server to shut down.
+ time.Sleep(2 * time.Second)
+
+ },
+ },
+}
+
+func testSetupFunc() func(env *testscript.Env) error {
+ return func(env *testscript.Env) error {
+ var keyVals []string
+ keyVals = append(keyVals, "HUGO_TESTRUN", "true")
+ hugoCachedDir := filepath.Join(env.WorkDir, "hugocache")
+ keyVals = append(keyVals, "HUGO_CACHEDIR", hugoCachedDir)
+
+ goVersion := runtime.Version()
+ // Strip all but the major and minor version.
+ goVersion = regexp.MustCompile(`^go(\d+\.\d+)`).FindStringSubmatch(goVersion)[1]
+ keyVals = append(keyVals, "GOVERSION", goVersion)
+ envhelpers.SetEnvVars(&env.Vars, keyVals...)
+
+ return nil
+ }
+}