github.com/ubuntu/ubuntu-report@v1.7.4-0.20240410144652-96f37d845fac/internal/metrics/metrics_test.go (about)

     1  package metrics_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/ubuntu/ubuntu-report/internal/helper"
    11  	"github.com/ubuntu/ubuntu-report/internal/metrics"
    12  )
    13  
    14  func TestGetIDS(t *testing.T) {
    15  	t.Parallel()
    16  
    17  	testCases := []struct {
    18  		name string
    19  		root string
    20  
    21  		wantDistro  string
    22  		wantVersion string
    23  		wantErr     bool
    24  	}{
    25  		{"regular", "testdata/good", "ubuntu", "18.04", false},
    26  		{"doesn't exist", "testdata/none", "", "", true},
    27  		{"empty file", "testdata/empty", "", "", true},
    28  		{"missing distro", "testdata/missing-fields/ids/distro", "", "", true},
    29  		{"missing version", "testdata/missing-fields/ids/version", "", "", true},
    30  		{"missing both", "testdata/missing-fields/ids/both", "", "", true},
    31  		{"empty distro", "testdata/empty-fields/ids/distro", "", "", true},
    32  		{"empty version", "testdata/empty-fields/ids/version", "", "", true},
    33  		{"empty both", "testdata/empty-fields/ids/both", "", "", true},
    34  		{"garbage content", "testdata/garbage", "", "", true},
    35  	}
    36  	for _, tc := range testCases {
    37  		tc := tc // capture range variable for parallel execution
    38  		t.Run(tc.name, func(t *testing.T) {
    39  			t.Parallel()
    40  			a := helper.Asserter{T: t}
    41  
    42  			m := newTestMetrics(t, metrics.WithRootAt(tc.root))
    43  			d, v, err := m.GetIDS()
    44  
    45  			a.CheckWantedErr(err, tc.wantErr)
    46  			a.Equal(d, tc.wantDistro)
    47  			a.Equal(v, tc.wantVersion)
    48  		})
    49  	}
    50  }
    51  
    52  func TestCollect(t *testing.T) {
    53  	t.Parallel()
    54  
    55  	testCases := []struct {
    56  		name             string
    57  		root             string
    58  		caseGPU          string
    59  		caseCPU          string
    60  		caseScreen       string
    61  		casePartition    string
    62  		caseArchitecture string
    63  		caseLibc6        string
    64  		caseHwCap        string
    65  		env              map[string]string
    66  
    67  		// note that only an internal json package error can make it returning an error
    68  		wantErr bool
    69  	}{
    70  		{"regular",
    71  			"testdata/good", "one gpu", "regular", "one screen",
    72  			"one partition", "regular", "regular", "regular",
    73  			map[string]string{"XDG_CURRENT_DESKTOP": "some:thing", "XDG_SESSION_DESKTOP": "ubuntusession", "XDG_SESSION_TYPE": "x12", "LANG": "fr_FR.UTF-8", "LANGUAGE": "fr_FR.UTF-8"},
    74  			false},
    75  		{"empty",
    76  			"testdata/none", "empty", "empty", "empty", "empty", "empty", "empty", "empty",
    77  			nil,
    78  			false},
    79  	}
    80  	for _, tc := range testCases {
    81  		tc := tc // capture range variable for parallel execution
    82  		t.Run(tc.name, func(t *testing.T) {
    83  			t.Parallel()
    84  			a := helper.Asserter{T: t}
    85  
    86  			cmdGPU, cancel := newMockShortCmd(t, "lspci", "-n", tc.caseGPU)
    87  			defer cancel()
    88  			cmdCPU, cancel := newMockShortCmd(t, "lscpu", "-J", tc.caseCPU)
    89  			defer cancel()
    90  			cmdScreen, cancel := newMockShortCmd(t, "xrandr", tc.caseScreen)
    91  			defer cancel()
    92  			cmdPartition, cancel := newMockShortCmd(t, "df", tc.casePartition)
    93  			defer cancel()
    94  			cmdArchitecture, cancel := newMockShortCmd(t, "dpkg", "--print-architecture", tc.caseArchitecture)
    95  			defer cancel()
    96  			cmdLibc6, cancel := newMockShortCmd(t, "dpkg", "--status", "libc6", tc.caseHwCap)
    97  			defer cancel()
    98  			cmdHwCap, cancel := newMockShortCmd(t, "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2", "--help", tc.caseHwCap)
    99  			defer cancel()
   100  
   101  			m := newTestMetrics(t, metrics.WithRootAt(tc.root),
   102  				metrics.WithGPUInfoCommand(cmdGPU),
   103  				metrics.WithCPUInfoCommand(cmdCPU),
   104  				metrics.WithScreenInfoCommand(cmdScreen),
   105  				metrics.WithSpaceInfoCommand(cmdPartition),
   106  				metrics.WithArchitectureCommand(cmdArchitecture),
   107  				metrics.WithHwCapCommand(cmdHwCap),
   108  				metrics.WithLibc6Command(cmdLibc6),
   109  				metrics.WithMapForEnv(tc.env))
   110  			got, err := m.Collect()
   111  
   112  			want := helper.LoadOrUpdateGolden(t, filepath.Join(tc.root, "gold", "collect"), got, *metrics.Update)
   113  			a.CheckWantedErr(err, tc.wantErr)
   114  			a.Equal(got, want)
   115  		})
   116  	}
   117  }
   118  
   119  func TestRunCollectTwice(t *testing.T) {
   120  	t.Parallel()
   121  
   122  	testCases := []struct {
   123  		name             string
   124  		root             string
   125  		caseGPU          string
   126  		caseCPU          string
   127  		caseScreen       string
   128  		casePartition    string
   129  		caseArchitecture string
   130  		caseLibc6        string
   131  		caseHwCap        string
   132  		env              map[string]string
   133  
   134  		// note that only an internal json package error can make it returning an error
   135  		wantErr bool
   136  	}{
   137  		{"regular",
   138  			"testdata/good", "one gpu", "regular", "one screen",
   139  			"one partition", "regular", "regular", "regular",
   140  			map[string]string{"XDG_CURRENT_DESKTOP": "some:thing", "XDG_SESSION_DESKTOP": "ubuntusession", "XDG_SESSION_TYPE": "x12", "LANG": "fr_FR.UTF-8", "LANGUAGE": "fr_FR.UTF-8"},
   141  			false},
   142  		{"empty",
   143  			"testdata/none", "empty", "empty", "empty", "empty", "empty", "empty", "empty",
   144  			nil,
   145  			false},
   146  	}
   147  	for _, tc := range testCases {
   148  		tc := tc // capture range variable for parallel execution
   149  		t.Run(tc.name, func(t *testing.T) {
   150  			t.Parallel()
   151  			a := helper.Asserter{T: t}
   152  
   153  			cmdGPU, cancel := newMockShortCmd(t, "lspci", "-n", tc.caseGPU)
   154  			defer cancel()
   155  			cmdCPU, cancel := newMockShortCmd(t, "lscpu", "-J", tc.caseCPU)
   156  			defer cancel()
   157  			cmdScreen, cancel := newMockShortCmd(t, "xrandr", tc.caseScreen)
   158  			defer cancel()
   159  			cmdPartition, cancel := newMockShortCmd(t, "df", tc.casePartition)
   160  			defer cancel()
   161  			cmdArchitecture, cancel := newMockShortCmd(t, "dpkg", "--print-architecture", tc.caseArchitecture)
   162  			defer cancel()
   163  			cmdLibc6, cancel := newMockShortCmd(t, "dpkg", "--status", "libc6", tc.caseHwCap)
   164  			defer cancel()
   165  			cmdHwCap, cancel := newMockShortCmd(t, "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2", "--help", tc.caseHwCap)
   166  			defer cancel()
   167  
   168  			m := newTestMetrics(t, metrics.WithRootAt(tc.root),
   169  				metrics.WithGPUInfoCommand(cmdGPU),
   170  				metrics.WithCPUInfoCommand(cmdCPU),
   171  				metrics.WithScreenInfoCommand(cmdScreen),
   172  				metrics.WithSpaceInfoCommand(cmdPartition),
   173  				metrics.WithArchitectureCommand(cmdArchitecture),
   174  				metrics.WithHwCapCommand(cmdHwCap),
   175  				metrics.WithLibc6Command(cmdLibc6),
   176  				metrics.WithMapForEnv(tc.env))
   177  			b1, err1 := m.Collect()
   178  
   179  			cmdGPU, cancel = newMockShortCmd(t, "lspci", "-n", tc.caseGPU)
   180  			defer cancel()
   181  			cmdCPU, cancel = newMockShortCmd(t, "lscpu", "-J", tc.caseCPU)
   182  			defer cancel()
   183  			cmdScreen, cancel = newMockShortCmd(t, "xrandr", tc.caseScreen)
   184  			defer cancel()
   185  			cmdPartition, cancel = newMockShortCmd(t, "df", tc.casePartition)
   186  			defer cancel()
   187  			cmdArchitecture, cancel = newMockShortCmd(t, "dpkg", "--print-architecture", tc.caseArchitecture)
   188  			defer cancel()
   189  			cmdLibc6, cancel = newMockShortCmd(t, "dpkg", "--status", "libc6", tc.caseHwCap)
   190  			defer cancel()
   191  			cmdHwCap, cancel = newMockShortCmd(t, "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2", "--help", tc.caseHwCap)
   192  			defer cancel()
   193  			m = newTestMetrics(t, metrics.WithRootAt(tc.root),
   194  				metrics.WithGPUInfoCommand(cmdGPU),
   195  				metrics.WithCPUInfoCommand(cmdCPU),
   196  				metrics.WithScreenInfoCommand(cmdScreen),
   197  				metrics.WithSpaceInfoCommand(cmdPartition),
   198  				metrics.WithArchitectureCommand(cmdArchitecture),
   199  				metrics.WithHwCapCommand(cmdHwCap),
   200  				metrics.WithLibc6Command(cmdLibc6),
   201  				metrics.WithMapForEnv(tc.env))
   202  			b2, err2 := m.Collect()
   203  
   204  			a.CheckWantedErr(err1, tc.wantErr)
   205  			a.CheckWantedErr(err2, tc.wantErr)
   206  			var got1, got2 json.RawMessage
   207  			json.Unmarshal(b1, &got1)
   208  			json.Unmarshal(b2, &got2)
   209  
   210  			a.Equal(got1, got2)
   211  		})
   212  	}
   213  }
   214  
   215  func newTestMetrics(t *testing.T, fixtures ...func(m *metrics.Metrics) error) metrics.Metrics {
   216  	t.Helper()
   217  	m, err := metrics.New(fixtures...)
   218  	if err != nil {
   219  		t.Fatal("can't create metrics object", err)
   220  	}
   221  	return m
   222  }
   223  
   224  func newMockShortCmd(t *testing.T, s ...string) (*exec.Cmd, context.CancelFunc) {
   225  	t.Helper()
   226  	return helper.ShortProcess(t, "TestMetricsHelperProcess", s...)
   227  }