github.com/linuxboot/fiano@v1.2.0/integration/utk_test.go (about)

     1  // Copyright 2018 the LinuxBoot 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 utk_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/linuxboot/fiano/pkg/uefi"
    17  	"github.com/linuxboot/fiano/pkg/utk"
    18  	"github.com/linuxboot/fiano/pkg/visitors"
    19  )
    20  
    21  // Returns all the ROM names (files which end in .rom) inside the roms folder.
    22  func romList(t *testing.T) []string {
    23  	roms, err := filepath.Glob("roms/*.rom")
    24  	if err != nil {
    25  		t.Fatalf("could not glob roms/*.rom, %v", err)
    26  	}
    27  	if len(roms) == 0 {
    28  		t.Fatal("no ROMs found with roms/*.rom")
    29  	}
    30  	return roms
    31  }
    32  
    33  // Create a temporary directory.
    34  func createTempDir(t *testing.T) string {
    35  	// Create temporary directory for test files.
    36  	tmpDir, err := os.MkdirTemp("", "utk-test")
    37  	if err != nil {
    38  		t.Fatalf("could not create temp dir: %v", err)
    39  	}
    40  	return tmpDir
    41  }
    42  
    43  // TestExtractAssembleExtract tests the extract and assemble subcommand of UTK.
    44  // The subcommands are run in this order:
    45  //
    46  // 1. utk extract tt.rom dir1
    47  // 2. utk assemble dir1 tmp.rom
    48  // 3. utk extract tmp.rom dir2
    49  //
    50  // The test passes iff the contents or dir1 and dir2 recursively equal. This
    51  // roundabout method is used because UTK can re-assemble a ROM image which is
    52  // logically equal to the original, but not bitwise equal (due to a different
    53  // compression algorithm being used). To compare the ROMs logically, step 3 is
    54  // required to decompresses it.
    55  func TestExtractAssembleExtract(t *testing.T) {
    56  	// Create a temporary directory.
    57  	tmpDir := createTempDir(t)
    58  	// For debugging, uncomment the next line and comment out os.RemoveAll
    59  	// t.Logf("temp %v", tmpDir)
    60  	defer os.RemoveAll(tmpDir)
    61  
    62  	for _, tt := range romList(t) {
    63  		t.Run(tt, func(t *testing.T) {
    64  			tmpDirT := filepath.Join(tmpDir, filepath.Base(tt))
    65  			if err := os.Mkdir(tmpDirT, 0777); err != nil {
    66  				t.Fatal(err)
    67  			}
    68  
    69  			// Test paths
    70  			var (
    71  				dir1         = filepath.Join(tmpDirT, "dir1")
    72  				tmpRom       = filepath.Join(tmpDirT, "tmp.rom")
    73  				dir2         = filepath.Join(tmpDirT, "dir2")
    74  				summary1Json = filepath.Join(dir1, "summary.json")
    75  				summary2Json = filepath.Join(dir2, "summary.json")
    76  			)
    77  
    78  			// Extract
    79  			if err := utk.Run(tt, "extract", dir1); err != nil {
    80  				t.Fatal(err)
    81  			}
    82  			// Assemble
    83  			if err := utk.Run(dir1, "save", tmpRom); err != nil {
    84  				t.Fatal(err)
    85  			}
    86  			// Extract
    87  			if err := utk.Run(tmpRom, "extract", dir2); err != nil {
    88  				t.Fatal(err)
    89  			}
    90  
    91  			// Output directories must not be empty.
    92  			for _, d := range []string{dir1, dir2} {
    93  				files, err := os.ReadDir(d)
    94  				if err != nil {
    95  					t.Fatalf("cannot read directory %q: %v", d, err)
    96  				}
    97  				if len(files) == 0 {
    98  					t.Errorf("no files in directory %q", d)
    99  				}
   100  			}
   101  
   102  			sedRemove := func(path string) {
   103  				sedCmd := exec.Command("sed", "-i", "/\"Size\": [0-9]*.*/d", path)
   104  				sedCmd.Stderr = os.Stderr
   105  				sedCmd.Stdout = os.Stdout
   106  				if err := sedCmd.Run(); err != nil {
   107  					t.Error(fmt.Sprintf("Sed failed for %s, error: %s", path, err.Error()))
   108  				}
   109  			}
   110  			// Remove all occurences of Size from JSON file
   111  			// compressed sizes are different
   112  			// diff will always fail if this is not done.
   113  			sedRemove(summary1Json)
   114  			sedRemove(summary2Json)
   115  
   116  			// Recursively test for equality.
   117  			cmd := exec.Command("diff", "-r", dir1, dir2)
   118  			cmd.Stderr = os.Stderr
   119  			cmd.Stdout = os.Stdout
   120  			if err := cmd.Run(); err != nil {
   121  				t.Error("directories did not recursively compare equal")
   122  			}
   123  		})
   124  	}
   125  }
   126  
   127  // TestRegressionJson tests for regression in the JSON. After making a change
   128  // which affects the tree, you must commit changes to the golden JSON files
   129  // with:
   130  //
   131  //     utk integration/roms/OVMF.rom json > integration/roms/OVMF.json
   132  //
   133  // Otherwise, this test will fail. This gives you a chance to review how your
   134  // code affects the tree and identify any mistakes.
   135  func TestRegressionJson(t *testing.T) {
   136  	// Create a temporary directory.
   137  	tmpDir := createTempDir(t)
   138  	defer os.RemoveAll(tmpDir)
   139  
   140  	for _, tt := range romList(t) {
   141  		t.Run(tt, func(t *testing.T) {
   142  			goldenJSONFile := strings.TrimSuffix(tt, ".rom") + ".json"
   143  			newJSONFile := filepath.Join(tmpDir, filepath.Base(goldenJSONFile))
   144  			if _, err := os.Stat(goldenJSONFile); os.IsNotExist(err) {
   145  				t.Skip("skipping test because no golden JSON file exists")
   146  			}
   147  
   148  			// Read and parse the image.
   149  			image, err := os.ReadFile(tt)
   150  			if err != nil {
   151  				t.Fatal(err)
   152  			}
   153  			parsedRoot, err := uefi.Parse(image)
   154  			if err != nil {
   155  				t.Fatal(err)
   156  			}
   157  
   158  			buf := &bytes.Buffer{}
   159  			json := &visitors.JSON{W: buf}
   160  			if err := json.Run(parsedRoot); err != nil {
   161  				t.Fatal(err)
   162  			}
   163  			if buf.String() == "" || buf.String() == "null" {
   164  				t.Fatal("no json")
   165  			}
   166  			if err := os.WriteFile(newJSONFile, buf.Bytes(), 0666); err != nil {
   167  				t.Fatal(err)
   168  			}
   169  
   170  			// Print diff.
   171  			cmd := exec.Command("diff", goldenJSONFile, newJSONFile)
   172  			cmd.Stdout = os.Stdout
   173  			if err := cmd.Run(); err != nil {
   174  				t.Error("json files did not compare equal")
   175  			}
   176  		})
   177  	}
   178  }