github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/grub/echo_test.go (about)

     1  // Copyright 2017-2020 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package grub
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"flag"
    11  	"fmt"
    12  	"net/url"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/mvdan/u-root-coreutils/pkg/curl"
    20  	"github.com/mvdan/u-root-coreutils/pkg/mount"
    21  	"github.com/mvdan/u-root-coreutils/pkg/mount/block"
    22  )
    23  
    24  var update = flag.Bool("run-bash", false, "run bash and update golden file")
    25  
    26  // TestMain is used to warp a go utility in the same test binary
    27  // when the environment variable BE_ECHO is set to 1, the binary will echo its
    28  // parameters using the %#v format string, so the parameters are escaped and can
    29  // be recovered.
    30  func TestMain(m *testing.M) {
    31  	if os.Getenv("BE_ECHO") == "1" {
    32  		fmt.Printf("echo:%#v\n", os.Args[1:])
    33  		return
    34  	} // call flag.Parse() here if TestMain uses flags
    35  	os.Exit(m.Run())
    36  }
    37  
    38  // TestHelperEcho tests the echo wrapper in TestMain
    39  func TestHelperEcho(t *testing.T) {
    40  	cmd := exec.Command(os.Args[0], "echothis")
    41  	cmd.Env = append(os.Environ(), "BE_ECHO=1")
    42  	out, err := cmd.Output()
    43  	t.Logf("%q\n", out)
    44  	if err != nil {
    45  		t.Fatalf("process ran with err %v", err)
    46  	}
    47  	want := "echo:[]string{\"echothis\"}\n"
    48  	if string(out) != want {
    49  		t.Fatalf("wrong process output got `%s` want `%s`", out, want)
    50  	}
    51  }
    52  
    53  // TestBashWrapper tests that the "./testdata/bash_wrapper.sh" works as expected
    54  // bash_wrapper.sh is a script that replace the internal command echo with its
    55  // first argument and source its second argument.
    56  // The goal is to be able to run grub's tests scripts, see TestGrubTests
    57  func TestBashWrapper(t *testing.T) {
    58  	if !*update {
    59  		t.Skip("use -run-bash flag to run this")
    60  	}
    61  	cmd := exec.Command("./testdata/bash_wrapper.sh", os.Args[0], "./testdata/test_bash_wrapper.sh")
    62  	cmd.Env = append(os.Environ(), "BE_ECHO=1")
    63  	out, err := cmd.Output()
    64  	t.Logf("%q\n", out)
    65  	if err != nil {
    66  		t.Fatalf("process ran with err %v", err)
    67  	}
    68  	want := "echo:[]string{\"param1\", \"param2\"}\n"
    69  	if string(out) != want {
    70  		t.Fatalf("wrong process output got `%s` want `%s`", out, want)
    71  	}
    72  }
    73  
    74  // TestGrubTests run tests imported from grub source to check our parser
    75  // grub has tests in for of scripts that are run both by grub and bash, they
    76  // mostly use echo and the test then compare the output of both runs.
    77  // In our case we don't want to compare the output of echo, but get the token
    78  // passed to echo. So we replace the echo command in bash with the wrapper (see
    79  // above). We can then compare the bash output to our parser output.
    80  // Also to avoid keeping the dependency on bash, the output are saved in the
    81  // golden files. One must run the test with '-run-bash' to update the golden
    82  // files in case new tests are added or the echo format is changed.
    83  func TestGrubTests(t *testing.T) {
    84  	files, err := filepath.Glob("testdata/*.in")
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	for _, file := range files {
    89  		name := strings.TrimSuffix(filepath.Base(file), ".in")
    90  		t.Run(name, func(t *testing.T) {
    91  			golden := strings.TrimSuffix(file, ".in") + ".out"
    92  			var out []byte
    93  			if *update {
    94  				cmd := exec.Command("./testdata/bash_wrapper.sh", os.Args[0], file)
    95  				cmd.Env = append(os.Environ(), "BE_ECHO=1")
    96  				out, err = cmd.Output()
    97  				// t.Logf("%s\n", out)
    98  				if err != nil {
    99  					t.Fatalf("process ran with err %v", err)
   100  				}
   101  			} else {
   102  				out, err = os.ReadFile(golden)
   103  				if err != nil {
   104  					t.Fatalf("error loading file `%s`, %v", golden, err)
   105  				}
   106  			}
   107  			// parse with our parser and compare
   108  			var b bytes.Buffer
   109  			wd := &url.URL{
   110  				Scheme: "file",
   111  				Path:   "./testdata",
   112  			}
   113  			mountPool := &mount.Pool{}
   114  			c := newParser(wd, block.BlockDevices{}, mountPool, curl.DefaultSchemes)
   115  			c.W = &b
   116  
   117  			script, err := os.ReadFile(file)
   118  			if err != nil {
   119  				t.Fatalf("error loading file `%s`, %v", file, err)
   120  			}
   121  			err = c.append(context.Background(), string(script))
   122  			if err != nil {
   123  				t.Fatalf("error parsing file `%s`, %v", file, err)
   124  			}
   125  
   126  			if b.String() != string(out) {
   127  				t.Fatalf("wrong script parsing output got `%s` want `%s`", b.String(), string(out))
   128  			}
   129  			// update/create golden file on success
   130  			if *update {
   131  				err := os.WriteFile(golden, out, 0o644)
   132  				if err != nil {
   133  					t.Fatalf("error writing file `%s`, %v", file, err)
   134  				}
   135  			}
   136  		})
   137  
   138  	}
   139  }