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 }