diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-01-04 18:24:36 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-05-16 18:01:29 +0200 |
commit | 241b21b0fd34d91fccb2ce69874110dceae6f926 (patch) | |
tree | d4e0118eac7e9c42f065815447a70805f8d6ad3e /main_test.go | |
parent | 6aededf6b42011c3039f5f66487a89a8dd65e0e7 (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.go | 382 |
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 + } +} |