github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/integration/testcmd/gotest/uinit/gotest.go (about)

     1  // Copyright 2021 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 main
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/mvdan/u-root-coreutils/integration/testcmd/common"
    20  	"golang.org/x/sys/unix"
    21  )
    22  
    23  const individualTestTimeout = 25 * time.Second
    24  
    25  var coverProfile = flag.String("coverprofile", "", "Filename to write coverage data to")
    26  
    27  func walkTests(testRoot string, fn func(string, string)) error {
    28  	return filepath.Walk(testRoot, func(path string, info os.FileInfo, err error) error {
    29  		if !info.Mode().IsRegular() || !strings.HasSuffix(path, ".test") || err != nil {
    30  			return nil
    31  		}
    32  		t2, err := filepath.Rel(testRoot, path)
    33  		if err != nil {
    34  			return err
    35  		}
    36  		pkgName := filepath.Dir(t2)
    37  
    38  		fn(path, pkgName)
    39  		return nil
    40  	})
    41  }
    42  
    43  // AppendFile takes two filepaths and concatenates the files at those.
    44  func AppendFile(srcFile, targetFile string) error {
    45  	cov, err := os.Open(srcFile)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	defer cov.Close()
    50  
    51  	out, err := os.OpenFile(targetFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	if _, err := io.Copy(out, cov); err != nil {
    57  		if err := out.Close(); err != nil {
    58  			return err
    59  		}
    60  		return fmt.Errorf("error appending %s to %s: %v", srcFile, targetFile, err)
    61  	}
    62  
    63  	return out.Close()
    64  }
    65  
    66  // runTest mounts a vfat or 9pfs volume and runs the tests within.
    67  func runTest() error {
    68  	flag.Parse()
    69  
    70  	if err := os.MkdirAll("/testdata", 0755); err != nil {
    71  		log.Fatalf("Couldn't create testdata directory: %v", err)
    72  	}
    73  
    74  	cleanup, err := common.MountSharedDir()
    75  	if err != nil {
    76  		return err
    77  	}
    78  	defer cleanup()
    79  	defer common.CollectKernelCoverage()
    80  
    81  	walkTests("/testdata/tests", func(path, pkgName string) {
    82  		// Send the kill signal with a 500ms grace period.
    83  		ctx, cancel := context.WithTimeout(context.Background(), individualTestTimeout+500*time.Millisecond)
    84  		defer cancel()
    85  
    86  		r, w, err := os.Pipe()
    87  		if err != nil {
    88  			log.Printf("Failed to get pipe: %v", err)
    89  			return
    90  		}
    91  
    92  		args := []string{"-test.v", "-test.bench=.", "-test.run=."}
    93  		coverFile := filepath.Join(filepath.Dir(path), "coverage.txt")
    94  		if len(*coverProfile) > 0 {
    95  			args = append(args, "-test.coverprofile", coverFile)
    96  		}
    97  
    98  		cmd := exec.CommandContext(ctx, path, args...)
    99  		cmd.Stdin, cmd.Stderr = os.Stdin, os.Stderr
   100  
   101  		// Write to stdout for humans, write to w for the JSON converter.
   102  		//
   103  		// The test collector will gobble up JSON for statistics, and
   104  		// print non-JSON for humans to consume.
   105  		cmd.Stdout = io.MultiWriter(os.Stdout, w)
   106  
   107  		// Start test in its own dir so that testdata is available as a
   108  		// relative directory.
   109  		cmd.Dir = filepath.Dir(path)
   110  		if err := cmd.Start(); err != nil {
   111  			log.Printf("Failed to start %q: %v", path, err)
   112  			return
   113  		}
   114  
   115  		// The test2json is not run with a context as it does not
   116  		// block. If we cancelled test2json with the same context as
   117  		// the test, we may lose some of the last few lines.
   118  		j := exec.Command("test2json", "-t", "-p", pkgName)
   119  		j.Stdin = r
   120  		j.Stdout, cmd.Stderr = os.Stdout, os.Stderr
   121  		if err := j.Start(); err != nil {
   122  			log.Printf("Failed to start test2json: %v", err)
   123  			return
   124  		}
   125  
   126  		if err := cmd.Wait(); err != nil {
   127  			// Log for processing by test2json.
   128  			fmt.Fprintf(w, "Error: test for %q exited early: %v", pkgName, err)
   129  		}
   130  
   131  		// Close the pipe so test2json will quit.
   132  		if err := w.Close(); err != nil {
   133  			log.Printf("Failed to close pipe: %v", err)
   134  		}
   135  		if err := j.Wait(); err != nil {
   136  			log.Printf("Failed to stop test2json: %v", err)
   137  		}
   138  
   139  		if err := AppendFile(coverFile, *coverProfile); err != nil {
   140  			log.Printf("Could not append to cover file: %v", err)
   141  		}
   142  	})
   143  	return nil
   144  }
   145  
   146  func main() {
   147  	if err := runTest(); err != nil {
   148  		log.Printf("Tests failed: %v", err)
   149  	} else {
   150  		// The test infra is expecting this exact print.
   151  		log.Print("TESTS PASSED MARKER")
   152  	}
   153  
   154  	if err := unix.Reboot(unix.LINUX_REBOOT_CMD_POWER_OFF); err != nil {
   155  		log.Fatalf("Failed to reboot: %v", err)
   156  	}
   157  }