github.com/muyo/sno@v1.2.1/internal/cpu_amd64_test.go (about)

     1  package internal
     2  
     3  import (
     4  	"testing"
     5  )
     6  
     7  func testCPU(t *testing.T) {
     8  	t.Run("real", testCPUReal)
     9  	t.Run("mocked", testCPUMocked)
    10  }
    11  
    12  // First tests are run against the real hardware and actual cpuid instruction.
    13  // While we can't reliably assume the availability of the instruction sets,
    14  // at the very least we may catch anomalies when the highest function parameter
    15  // returned is not sane - or when SSE2 instructions are not available where we
    16  // assume they should be.
    17  func testCPUReal(t *testing.T) {
    18  	t.Run("highest-function-parameter-valid", testCPURealMFIValid)
    19  	t.Run("has-base-set", testCPURealHasBaseSet)
    20  	t.Run("has-vector-support-attempt", testCPURealHasVectorSupportAttempt)
    21  }
    22  
    23  func testCPURealMFIValid(t *testing.T) {
    24  	eax, _, _, _ := cpuid(0)
    25  	if eax < 1 {
    26  		t.Errorf("expected a non-zero highest function parameter, got [%d]", eax)
    27  	}
    28  }
    29  
    30  func testCPURealHasBaseSet(t *testing.T) {
    31  	_, _, _, edx := cpuid(1)
    32  	if (edx & (1 << 26)) == 0 {
    33  		t.Error("expected the SSE2 instruction set to be available, does not appear to be")
    34  	}
    35  }
    36  
    37  func testCPURealHasVectorSupportAttempt(t *testing.T) {
    38  	defer func() {
    39  		catch(t, recover(), "")
    40  	}()
    41  
    42  	// Note: We don't care about the result as we can't assume to get a 'true'.
    43  	// We only care for this to not panic.
    44  	checkVectorSupport()
    45  }
    46  
    47  // Note: Those tests must not run in parallel to any tests that rely
    48  // on real hardware and the actual cpuid implementation (vide enc/dec),
    49  // as the cpuid function gets swapped out for mocks.
    50  func testCPUMocked(t *testing.T) {
    51  	cpuid = cpu.id
    52  
    53  	t.Run("highest-function-parameter-invalid", testCPUHasVectorSupportMFIInvalid)
    54  	t.Run("highest-function-parameter-too-low", testCPUHasVectorSupportMFILow)
    55  	t.Run("lacks-base-set", testCPUHasVectorSupportLacksBaseSet)
    56  	t.Run("lacks-extended-sets", testCPUHasVectorSupportLacksExtendedSets)
    57  	t.Run("passes", testCPUHasVectorPasses)
    58  
    59  	// Restore real implementation.
    60  	cpuid = cpuidReal
    61  }
    62  
    63  func testCPUHasVectorSupportMFIInvalid(t *testing.T) {
    64  	defer func() {
    65  		catch(t, recover(), cpuLacksSSE2ErrMsg)
    66  	}()
    67  
    68  	cpu.reset()
    69  	cpu.eax = 0
    70  	expectVectorSupport(t, false)
    71  }
    72  
    73  func testCPUHasVectorSupportMFILow(t *testing.T) {
    74  	defer func() {
    75  		catch(t, recover(), "")
    76  	}()
    77  
    78  	cpu.reset()
    79  	cpu.eax = 6
    80  	expectVectorSupport(t, false)
    81  }
    82  
    83  func testCPUHasVectorSupportLacksBaseSet(t *testing.T) {
    84  	defer func() {
    85  		catch(t, recover(), cpuLacksSSE2ErrMsg)
    86  	}()
    87  
    88  	cpu.reset()
    89  	cpu.edx ^= 1 << 26 // SSE2 is featured as 1 << 26, so we simply set everything *but*.
    90  	expectVectorSupport(t, false)
    91  }
    92  
    93  func testCPUHasVectorSupportLacksExtendedSets(t *testing.T) {
    94  	defer func() {
    95  		catch(t, recover(), "")
    96  	}()
    97  
    98  	for _, c := range []struct {
    99  		name string
   100  		ebx  uint32
   101  		ecx  uint32
   102  	}{
   103  		{"SSE3", 0, ^uint32(0x00000001)},
   104  		{"SSSE3", 0, ^uint32(0x00000200)},
   105  		{"SSE4", 0, ^uint32(0x00080000)},
   106  		{"SSE4.2", 0, ^uint32(0x00100000)},
   107  		{"BMI1", ^uint32(0x00000008), 0},
   108  		{"BMI2", ^uint32(0x00000100), 0},
   109  	} {
   110  		t.Run(c.name, func(t *testing.T) {
   111  			cpu.reset()
   112  			if c.ebx != 0 {
   113  				cpu.ebx = c.ebx
   114  			}
   115  
   116  			if c.ecx != 0 {
   117  				cpu.ecx = c.ecx
   118  			}
   119  
   120  			expectVectorSupport(t, false)
   121  		})
   122  	}
   123  }
   124  
   125  func testCPUHasVectorPasses(t *testing.T) {
   126  	defer func() {
   127  		catch(t, recover(), "")
   128  	}()
   129  
   130  	cpu.reset()
   131  	expectVectorSupport(t, true)
   132  }
   133  
   134  var cpu = func() *cpuMock {
   135  	c := &cpuMock{}
   136  	c.reset()
   137  
   138  	return c
   139  }()
   140  
   141  type cpuMock struct {
   142  	eax, ebx, ecx, edx uint32
   143  }
   144  
   145  func (c *cpuMock) reset() {
   146  	c.eax = 7
   147  	c.ebx = 0x00000108
   148  	c.ecx = 0x00180201
   149  	c.edx = 1 << 26
   150  }
   151  
   152  func (c *cpuMock) id(_ uint32) (eax, ebx, ecx, edx uint32) {
   153  	return c.eax, c.ebx, c.ecx, c.edx
   154  }
   155  
   156  func catch(t *testing.T, err interface{}, expected string) {
   157  	if expected != "" {
   158  		if err == nil {
   159  			t.Fatalf("expected a panic with message [%s]", expected)
   160  		}
   161  
   162  		if err != expected {
   163  			t.Errorf("expected a panic with message [%s], got [%s]", expected, err)
   164  		}
   165  
   166  		return
   167  	}
   168  
   169  	if err != nil {
   170  		t.Fatalf("expected to not panic, panicked with [%s]", err)
   171  	}
   172  }
   173  
   174  func expectVectorSupport(t *testing.T, expected bool) {
   175  	if actual := checkVectorSupport(); actual != expected {
   176  		t.Errorf("expected [%t], got [%t]", expected, actual)
   177  	}
   178  }