k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/cpumanager/topology/topology_test.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package topology
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	cadvisorapi "github.com/google/cadvisor/info/v1"
    24  	"github.com/google/go-cmp/cmp"
    25  	"k8s.io/utils/cpuset"
    26  )
    27  
    28  func Test_Discover(t *testing.T) {
    29  
    30  	tests := []struct {
    31  		name        string
    32  		machineInfo cadvisorapi.MachineInfo
    33  		want        *CPUTopology
    34  		wantErr     bool
    35  	}{
    36  		{
    37  			name: "FailNumCores",
    38  			machineInfo: cadvisorapi.MachineInfo{
    39  				NumCores: 0,
    40  			},
    41  			want:    &CPUTopology{},
    42  			wantErr: true,
    43  		},
    44  		{
    45  			name: "OneSocketHT",
    46  			machineInfo: cadvisorapi.MachineInfo{
    47  				NumCores:   8,
    48  				NumSockets: 1,
    49  				Topology: []cadvisorapi.Node{
    50  					{Id: 0,
    51  						Cores: []cadvisorapi.Core{
    52  							{SocketID: 0, Id: 0, Threads: []int{0, 4}},
    53  							{SocketID: 0, Id: 1, Threads: []int{1, 5}},
    54  							{SocketID: 0, Id: 2, Threads: []int{2, 6}},
    55  							{SocketID: 0, Id: 3, Threads: []int{3, 7}},
    56  						},
    57  					},
    58  				},
    59  			},
    60  			want: &CPUTopology{
    61  				NumCPUs:      8,
    62  				NumSockets:   1,
    63  				NumCores:     4,
    64  				NumNUMANodes: 1,
    65  				CPUDetails: map[int]CPUInfo{
    66  					0: {CoreID: 0, SocketID: 0, NUMANodeID: 0},
    67  					1: {CoreID: 1, SocketID: 0, NUMANodeID: 0},
    68  					2: {CoreID: 2, SocketID: 0, NUMANodeID: 0},
    69  					3: {CoreID: 3, SocketID: 0, NUMANodeID: 0},
    70  					4: {CoreID: 0, SocketID: 0, NUMANodeID: 0},
    71  					5: {CoreID: 1, SocketID: 0, NUMANodeID: 0},
    72  					6: {CoreID: 2, SocketID: 0, NUMANodeID: 0},
    73  					7: {CoreID: 3, SocketID: 0, NUMANodeID: 0},
    74  				},
    75  			},
    76  			wantErr: false,
    77  		},
    78  		{
    79  			// dual xeon gold 6230
    80  			name: "DualSocketMultiNumaPerSocketHT",
    81  			machineInfo: cadvisorapi.MachineInfo{
    82  				NumCores:   80,
    83  				NumSockets: 2,
    84  				Topology: []cadvisorapi.Node{
    85  					{Id: 0,
    86  						Cores: []cadvisorapi.Core{
    87  							{SocketID: 0, Id: 0, Threads: []int{0, 40}},
    88  							{SocketID: 0, Id: 1, Threads: []int{1, 41}},
    89  							{SocketID: 0, Id: 2, Threads: []int{2, 42}},
    90  							{SocketID: 0, Id: 8, Threads: []int{3, 43}},
    91  							{SocketID: 0, Id: 9, Threads: []int{4, 44}},
    92  							{SocketID: 0, Id: 16, Threads: []int{5, 45}},
    93  							{SocketID: 0, Id: 17, Threads: []int{6, 46}},
    94  							{SocketID: 0, Id: 18, Threads: []int{7, 47}},
    95  							{SocketID: 0, Id: 24, Threads: []int{8, 48}},
    96  							{SocketID: 0, Id: 25, Threads: []int{9, 49}},
    97  						},
    98  					},
    99  					{Id: 1,
   100  						Cores: []cadvisorapi.Core{
   101  							{SocketID: 0, Id: 3, Threads: []int{10, 50}},
   102  							{SocketID: 0, Id: 4, Threads: []int{11, 51}},
   103  							{SocketID: 0, Id: 10, Threads: []int{12, 52}},
   104  							{SocketID: 0, Id: 11, Threads: []int{13, 53}},
   105  							{SocketID: 0, Id: 12, Threads: []int{14, 54}},
   106  							{SocketID: 0, Id: 19, Threads: []int{15, 55}},
   107  							{SocketID: 0, Id: 20, Threads: []int{16, 56}},
   108  							{SocketID: 0, Id: 26, Threads: []int{17, 57}},
   109  							{SocketID: 0, Id: 27, Threads: []int{18, 58}},
   110  							{SocketID: 0, Id: 28, Threads: []int{19, 59}},
   111  						},
   112  					},
   113  					{Id: 2,
   114  						Cores: []cadvisorapi.Core{
   115  							{SocketID: 1, Id: 0, Threads: []int{20, 60}},
   116  							{SocketID: 1, Id: 1, Threads: []int{21, 61}},
   117  							{SocketID: 1, Id: 2, Threads: []int{22, 62}},
   118  							{SocketID: 1, Id: 8, Threads: []int{23, 63}},
   119  							{SocketID: 1, Id: 9, Threads: []int{24, 64}},
   120  							{SocketID: 1, Id: 16, Threads: []int{25, 65}},
   121  							{SocketID: 1, Id: 17, Threads: []int{26, 66}},
   122  							{SocketID: 1, Id: 18, Threads: []int{27, 67}},
   123  							{SocketID: 1, Id: 24, Threads: []int{28, 68}},
   124  							{SocketID: 1, Id: 25, Threads: []int{29, 69}},
   125  						},
   126  					},
   127  					{Id: 3,
   128  						Cores: []cadvisorapi.Core{
   129  							{SocketID: 1, Id: 3, Threads: []int{30, 70}},
   130  							{SocketID: 1, Id: 4, Threads: []int{31, 71}},
   131  							{SocketID: 1, Id: 10, Threads: []int{32, 72}},
   132  							{SocketID: 1, Id: 11, Threads: []int{33, 73}},
   133  							{SocketID: 1, Id: 12, Threads: []int{34, 74}},
   134  							{SocketID: 1, Id: 19, Threads: []int{35, 75}},
   135  							{SocketID: 1, Id: 20, Threads: []int{36, 76}},
   136  							{SocketID: 1, Id: 26, Threads: []int{37, 77}},
   137  							{SocketID: 1, Id: 27, Threads: []int{38, 78}},
   138  							{SocketID: 1, Id: 28, Threads: []int{39, 79}},
   139  						},
   140  					},
   141  				},
   142  			},
   143  			want: &CPUTopology{
   144  				NumCPUs:      80,
   145  				NumSockets:   2,
   146  				NumCores:     40,
   147  				NumNUMANodes: 4,
   148  				CPUDetails: map[int]CPUInfo{
   149  					0:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   150  					1:  {CoreID: 1, SocketID: 0, NUMANodeID: 0},
   151  					2:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   152  					3:  {CoreID: 3, SocketID: 0, NUMANodeID: 0},
   153  					4:  {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   154  					5:  {CoreID: 5, SocketID: 0, NUMANodeID: 0},
   155  					6:  {CoreID: 6, SocketID: 0, NUMANodeID: 0},
   156  					7:  {CoreID: 7, SocketID: 0, NUMANodeID: 0},
   157  					8:  {CoreID: 8, SocketID: 0, NUMANodeID: 0},
   158  					9:  {CoreID: 9, SocketID: 0, NUMANodeID: 0},
   159  					10: {CoreID: 10, SocketID: 0, NUMANodeID: 1},
   160  					11: {CoreID: 11, SocketID: 0, NUMANodeID: 1},
   161  					12: {CoreID: 12, SocketID: 0, NUMANodeID: 1},
   162  					13: {CoreID: 13, SocketID: 0, NUMANodeID: 1},
   163  					14: {CoreID: 14, SocketID: 0, NUMANodeID: 1},
   164  					15: {CoreID: 15, SocketID: 0, NUMANodeID: 1},
   165  					16: {CoreID: 16, SocketID: 0, NUMANodeID: 1},
   166  					17: {CoreID: 17, SocketID: 0, NUMANodeID: 1},
   167  					18: {CoreID: 18, SocketID: 0, NUMANodeID: 1},
   168  					19: {CoreID: 19, SocketID: 0, NUMANodeID: 1},
   169  					20: {CoreID: 20, SocketID: 1, NUMANodeID: 2},
   170  					21: {CoreID: 21, SocketID: 1, NUMANodeID: 2},
   171  					22: {CoreID: 22, SocketID: 1, NUMANodeID: 2},
   172  					23: {CoreID: 23, SocketID: 1, NUMANodeID: 2},
   173  					24: {CoreID: 24, SocketID: 1, NUMANodeID: 2},
   174  					25: {CoreID: 25, SocketID: 1, NUMANodeID: 2},
   175  					26: {CoreID: 26, SocketID: 1, NUMANodeID: 2},
   176  					27: {CoreID: 27, SocketID: 1, NUMANodeID: 2},
   177  					28: {CoreID: 28, SocketID: 1, NUMANodeID: 2},
   178  					29: {CoreID: 29, SocketID: 1, NUMANodeID: 2},
   179  					30: {CoreID: 30, SocketID: 1, NUMANodeID: 3},
   180  					31: {CoreID: 31, SocketID: 1, NUMANodeID: 3},
   181  					32: {CoreID: 32, SocketID: 1, NUMANodeID: 3},
   182  					33: {CoreID: 33, SocketID: 1, NUMANodeID: 3},
   183  					34: {CoreID: 34, SocketID: 1, NUMANodeID: 3},
   184  					35: {CoreID: 35, SocketID: 1, NUMANodeID: 3},
   185  					36: {CoreID: 36, SocketID: 1, NUMANodeID: 3},
   186  					37: {CoreID: 37, SocketID: 1, NUMANodeID: 3},
   187  					38: {CoreID: 38, SocketID: 1, NUMANodeID: 3},
   188  					39: {CoreID: 39, SocketID: 1, NUMANodeID: 3},
   189  					40: {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   190  					41: {CoreID: 1, SocketID: 0, NUMANodeID: 0},
   191  					42: {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   192  					43: {CoreID: 3, SocketID: 0, NUMANodeID: 0},
   193  					44: {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   194  					45: {CoreID: 5, SocketID: 0, NUMANodeID: 0},
   195  					46: {CoreID: 6, SocketID: 0, NUMANodeID: 0},
   196  					47: {CoreID: 7, SocketID: 0, NUMANodeID: 0},
   197  					48: {CoreID: 8, SocketID: 0, NUMANodeID: 0},
   198  					49: {CoreID: 9, SocketID: 0, NUMANodeID: 0},
   199  					50: {CoreID: 10, SocketID: 0, NUMANodeID: 1},
   200  					51: {CoreID: 11, SocketID: 0, NUMANodeID: 1},
   201  					52: {CoreID: 12, SocketID: 0, NUMANodeID: 1},
   202  					53: {CoreID: 13, SocketID: 0, NUMANodeID: 1},
   203  					54: {CoreID: 14, SocketID: 0, NUMANodeID: 1},
   204  					55: {CoreID: 15, SocketID: 0, NUMANodeID: 1},
   205  					56: {CoreID: 16, SocketID: 0, NUMANodeID: 1},
   206  					57: {CoreID: 17, SocketID: 0, NUMANodeID: 1},
   207  					58: {CoreID: 18, SocketID: 0, NUMANodeID: 1},
   208  					59: {CoreID: 19, SocketID: 0, NUMANodeID: 1},
   209  					60: {CoreID: 20, SocketID: 1, NUMANodeID: 2},
   210  					61: {CoreID: 21, SocketID: 1, NUMANodeID: 2},
   211  					62: {CoreID: 22, SocketID: 1, NUMANodeID: 2},
   212  					63: {CoreID: 23, SocketID: 1, NUMANodeID: 2},
   213  					64: {CoreID: 24, SocketID: 1, NUMANodeID: 2},
   214  					65: {CoreID: 25, SocketID: 1, NUMANodeID: 2},
   215  					66: {CoreID: 26, SocketID: 1, NUMANodeID: 2},
   216  					67: {CoreID: 27, SocketID: 1, NUMANodeID: 2},
   217  					68: {CoreID: 28, SocketID: 1, NUMANodeID: 2},
   218  					69: {CoreID: 29, SocketID: 1, NUMANodeID: 2},
   219  					70: {CoreID: 30, SocketID: 1, NUMANodeID: 3},
   220  					71: {CoreID: 31, SocketID: 1, NUMANodeID: 3},
   221  					72: {CoreID: 32, SocketID: 1, NUMANodeID: 3},
   222  					73: {CoreID: 33, SocketID: 1, NUMANodeID: 3},
   223  					74: {CoreID: 34, SocketID: 1, NUMANodeID: 3},
   224  					75: {CoreID: 35, SocketID: 1, NUMANodeID: 3},
   225  					76: {CoreID: 36, SocketID: 1, NUMANodeID: 3},
   226  					77: {CoreID: 37, SocketID: 1, NUMANodeID: 3},
   227  					78: {CoreID: 38, SocketID: 1, NUMANodeID: 3},
   228  					79: {CoreID: 39, SocketID: 1, NUMANodeID: 3},
   229  				},
   230  			},
   231  			wantErr: false,
   232  		},
   233  		{
   234  
   235  			// FAKE Topology from dual xeon gold 6230
   236  			// (see: dual xeon gold 6230).
   237  			// We flip NUMA cells and Sockets to exercise the code.
   238  			// TODO(fromanirh): replace with a real-world topology
   239  			// once we find a suitable one.
   240  			// Note: this is a fake topology. Thus, there is not a "correct"
   241  			// representation. This one was created following the these concepts:
   242  			// 1. be internally consistent (most important rule)
   243  			// 2. be as close as possible as existing HW topologies
   244  			// 3. if possible, minimize chances wrt existing HW topologies.
   245  			name: "DualNumaMultiSocketPerNumaHT",
   246  			machineInfo: cadvisorapi.MachineInfo{
   247  				NumCores:   80,
   248  				NumSockets: 4,
   249  				Topology: []cadvisorapi.Node{
   250  					{Id: 0,
   251  						Cores: []cadvisorapi.Core{
   252  							{SocketID: 0, Id: 0, Threads: []int{0, 40}},
   253  							{SocketID: 0, Id: 1, Threads: []int{1, 41}},
   254  							{SocketID: 0, Id: 2, Threads: []int{2, 42}},
   255  							{SocketID: 0, Id: 8, Threads: []int{3, 43}},
   256  							{SocketID: 0, Id: 9, Threads: []int{4, 44}},
   257  							{SocketID: 0, Id: 16, Threads: []int{5, 45}},
   258  							{SocketID: 0, Id: 17, Threads: []int{6, 46}},
   259  							{SocketID: 0, Id: 18, Threads: []int{7, 47}},
   260  							{SocketID: 0, Id: 24, Threads: []int{8, 48}},
   261  							{SocketID: 0, Id: 25, Threads: []int{9, 49}},
   262  							{SocketID: 1, Id: 3, Threads: []int{10, 50}},
   263  							{SocketID: 1, Id: 4, Threads: []int{11, 51}},
   264  							{SocketID: 1, Id: 10, Threads: []int{12, 52}},
   265  							{SocketID: 1, Id: 11, Threads: []int{13, 53}},
   266  							{SocketID: 1, Id: 12, Threads: []int{14, 54}},
   267  							{SocketID: 1, Id: 19, Threads: []int{15, 55}},
   268  							{SocketID: 1, Id: 20, Threads: []int{16, 56}},
   269  							{SocketID: 1, Id: 26, Threads: []int{17, 57}},
   270  							{SocketID: 1, Id: 27, Threads: []int{18, 58}},
   271  							{SocketID: 1, Id: 28, Threads: []int{19, 59}},
   272  						},
   273  					},
   274  					{Id: 1,
   275  						Cores: []cadvisorapi.Core{
   276  							{SocketID: 2, Id: 0, Threads: []int{20, 60}},
   277  							{SocketID: 2, Id: 1, Threads: []int{21, 61}},
   278  							{SocketID: 2, Id: 2, Threads: []int{22, 62}},
   279  							{SocketID: 2, Id: 8, Threads: []int{23, 63}},
   280  							{SocketID: 2, Id: 9, Threads: []int{24, 64}},
   281  							{SocketID: 2, Id: 16, Threads: []int{25, 65}},
   282  							{SocketID: 2, Id: 17, Threads: []int{26, 66}},
   283  							{SocketID: 2, Id: 18, Threads: []int{27, 67}},
   284  							{SocketID: 2, Id: 24, Threads: []int{28, 68}},
   285  							{SocketID: 2, Id: 25, Threads: []int{29, 69}},
   286  							{SocketID: 3, Id: 3, Threads: []int{30, 70}},
   287  							{SocketID: 3, Id: 4, Threads: []int{31, 71}},
   288  							{SocketID: 3, Id: 10, Threads: []int{32, 72}},
   289  							{SocketID: 3, Id: 11, Threads: []int{33, 73}},
   290  							{SocketID: 3, Id: 12, Threads: []int{34, 74}},
   291  							{SocketID: 3, Id: 19, Threads: []int{35, 75}},
   292  							{SocketID: 3, Id: 20, Threads: []int{36, 76}},
   293  							{SocketID: 3, Id: 26, Threads: []int{37, 77}},
   294  							{SocketID: 3, Id: 27, Threads: []int{38, 78}},
   295  							{SocketID: 3, Id: 28, Threads: []int{39, 79}},
   296  						},
   297  					},
   298  				},
   299  			},
   300  			want: &CPUTopology{
   301  				NumCPUs:      80,
   302  				NumSockets:   4,
   303  				NumCores:     40,
   304  				NumNUMANodes: 2,
   305  				CPUDetails: map[int]CPUInfo{
   306  					0:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   307  					1:  {CoreID: 1, SocketID: 0, NUMANodeID: 0},
   308  					2:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   309  					3:  {CoreID: 3, SocketID: 0, NUMANodeID: 0},
   310  					4:  {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   311  					5:  {CoreID: 5, SocketID: 0, NUMANodeID: 0},
   312  					6:  {CoreID: 6, SocketID: 0, NUMANodeID: 0},
   313  					7:  {CoreID: 7, SocketID: 0, NUMANodeID: 0},
   314  					8:  {CoreID: 8, SocketID: 0, NUMANodeID: 0},
   315  					9:  {CoreID: 9, SocketID: 0, NUMANodeID: 0},
   316  					10: {CoreID: 10, SocketID: 1, NUMANodeID: 0},
   317  					11: {CoreID: 11, SocketID: 1, NUMANodeID: 0},
   318  					12: {CoreID: 12, SocketID: 1, NUMANodeID: 0},
   319  					13: {CoreID: 13, SocketID: 1, NUMANodeID: 0},
   320  					14: {CoreID: 14, SocketID: 1, NUMANodeID: 0},
   321  					15: {CoreID: 15, SocketID: 1, NUMANodeID: 0},
   322  					16: {CoreID: 16, SocketID: 1, NUMANodeID: 0},
   323  					17: {CoreID: 17, SocketID: 1, NUMANodeID: 0},
   324  					18: {CoreID: 18, SocketID: 1, NUMANodeID: 0},
   325  					19: {CoreID: 19, SocketID: 1, NUMANodeID: 0},
   326  					20: {CoreID: 20, SocketID: 2, NUMANodeID: 1},
   327  					21: {CoreID: 21, SocketID: 2, NUMANodeID: 1},
   328  					22: {CoreID: 22, SocketID: 2, NUMANodeID: 1},
   329  					23: {CoreID: 23, SocketID: 2, NUMANodeID: 1},
   330  					24: {CoreID: 24, SocketID: 2, NUMANodeID: 1},
   331  					25: {CoreID: 25, SocketID: 2, NUMANodeID: 1},
   332  					26: {CoreID: 26, SocketID: 2, NUMANodeID: 1},
   333  					27: {CoreID: 27, SocketID: 2, NUMANodeID: 1},
   334  					28: {CoreID: 28, SocketID: 2, NUMANodeID: 1},
   335  					29: {CoreID: 29, SocketID: 2, NUMANodeID: 1},
   336  					30: {CoreID: 30, SocketID: 3, NUMANodeID: 1},
   337  					31: {CoreID: 31, SocketID: 3, NUMANodeID: 1},
   338  					32: {CoreID: 32, SocketID: 3, NUMANodeID: 1},
   339  					33: {CoreID: 33, SocketID: 3, NUMANodeID: 1},
   340  					34: {CoreID: 34, SocketID: 3, NUMANodeID: 1},
   341  					35: {CoreID: 35, SocketID: 3, NUMANodeID: 1},
   342  					36: {CoreID: 36, SocketID: 3, NUMANodeID: 1},
   343  					37: {CoreID: 37, SocketID: 3, NUMANodeID: 1},
   344  					38: {CoreID: 38, SocketID: 3, NUMANodeID: 1},
   345  					39: {CoreID: 39, SocketID: 3, NUMANodeID: 1},
   346  					40: {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   347  					41: {CoreID: 1, SocketID: 0, NUMANodeID: 0},
   348  					42: {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   349  					43: {CoreID: 3, SocketID: 0, NUMANodeID: 0},
   350  					44: {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   351  					45: {CoreID: 5, SocketID: 0, NUMANodeID: 0},
   352  					46: {CoreID: 6, SocketID: 0, NUMANodeID: 0},
   353  					47: {CoreID: 7, SocketID: 0, NUMANodeID: 0},
   354  					48: {CoreID: 8, SocketID: 0, NUMANodeID: 0},
   355  					49: {CoreID: 9, SocketID: 0, NUMANodeID: 0},
   356  					50: {CoreID: 10, SocketID: 1, NUMANodeID: 0},
   357  					51: {CoreID: 11, SocketID: 1, NUMANodeID: 0},
   358  					52: {CoreID: 12, SocketID: 1, NUMANodeID: 0},
   359  					53: {CoreID: 13, SocketID: 1, NUMANodeID: 0},
   360  					54: {CoreID: 14, SocketID: 1, NUMANodeID: 0},
   361  					55: {CoreID: 15, SocketID: 1, NUMANodeID: 0},
   362  					56: {CoreID: 16, SocketID: 1, NUMANodeID: 0},
   363  					57: {CoreID: 17, SocketID: 1, NUMANodeID: 0},
   364  					58: {CoreID: 18, SocketID: 1, NUMANodeID: 0},
   365  					59: {CoreID: 19, SocketID: 1, NUMANodeID: 0},
   366  					60: {CoreID: 20, SocketID: 2, NUMANodeID: 1},
   367  					61: {CoreID: 21, SocketID: 2, NUMANodeID: 1},
   368  					62: {CoreID: 22, SocketID: 2, NUMANodeID: 1},
   369  					63: {CoreID: 23, SocketID: 2, NUMANodeID: 1},
   370  					64: {CoreID: 24, SocketID: 2, NUMANodeID: 1},
   371  					65: {CoreID: 25, SocketID: 2, NUMANodeID: 1},
   372  					66: {CoreID: 26, SocketID: 2, NUMANodeID: 1},
   373  					67: {CoreID: 27, SocketID: 2, NUMANodeID: 1},
   374  					68: {CoreID: 28, SocketID: 2, NUMANodeID: 1},
   375  					69: {CoreID: 29, SocketID: 2, NUMANodeID: 1},
   376  					70: {CoreID: 30, SocketID: 3, NUMANodeID: 1},
   377  					71: {CoreID: 31, SocketID: 3, NUMANodeID: 1},
   378  					72: {CoreID: 32, SocketID: 3, NUMANodeID: 1},
   379  					73: {CoreID: 33, SocketID: 3, NUMANodeID: 1},
   380  					74: {CoreID: 34, SocketID: 3, NUMANodeID: 1},
   381  					75: {CoreID: 35, SocketID: 3, NUMANodeID: 1},
   382  					76: {CoreID: 36, SocketID: 3, NUMANodeID: 1},
   383  					77: {CoreID: 37, SocketID: 3, NUMANodeID: 1},
   384  					78: {CoreID: 38, SocketID: 3, NUMANodeID: 1},
   385  					79: {CoreID: 39, SocketID: 3, NUMANodeID: 1},
   386  				},
   387  			},
   388  			wantErr: false,
   389  		},
   390  		{
   391  			name: "DualSocketNoHT",
   392  			machineInfo: cadvisorapi.MachineInfo{
   393  				NumCores:   4,
   394  				NumSockets: 2,
   395  				Topology: []cadvisorapi.Node{
   396  					{Id: 0,
   397  						Cores: []cadvisorapi.Core{
   398  							{SocketID: 0, Id: 0, Threads: []int{0}},
   399  							{SocketID: 0, Id: 2, Threads: []int{2}},
   400  						},
   401  					},
   402  					{Id: 1,
   403  						Cores: []cadvisorapi.Core{
   404  							{SocketID: 1, Id: 1, Threads: []int{1}},
   405  							{SocketID: 1, Id: 3, Threads: []int{3}},
   406  						},
   407  					},
   408  				},
   409  			},
   410  			want: &CPUTopology{
   411  				NumCPUs:      4,
   412  				NumSockets:   2,
   413  				NumCores:     4,
   414  				NumNUMANodes: 2,
   415  				CPUDetails: map[int]CPUInfo{
   416  					0: {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   417  					1: {CoreID: 1, SocketID: 1, NUMANodeID: 1},
   418  					2: {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   419  					3: {CoreID: 3, SocketID: 1, NUMANodeID: 1},
   420  				},
   421  			},
   422  			wantErr: false,
   423  		},
   424  		{
   425  			name: "DualSocketHT - non unique Core'ID's",
   426  			machineInfo: cadvisorapi.MachineInfo{
   427  				NumCores:   12,
   428  				NumSockets: 2,
   429  				Topology: []cadvisorapi.Node{
   430  					{Id: 0,
   431  						Cores: []cadvisorapi.Core{
   432  							{SocketID: 0, Id: 0, Threads: []int{0, 6}},
   433  							{SocketID: 0, Id: 1, Threads: []int{1, 7}},
   434  							{SocketID: 0, Id: 2, Threads: []int{2, 8}},
   435  						},
   436  					},
   437  					{Id: 1,
   438  						Cores: []cadvisorapi.Core{
   439  							{SocketID: 1, Id: 0, Threads: []int{3, 9}},
   440  							{SocketID: 1, Id: 1, Threads: []int{4, 10}},
   441  							{SocketID: 1, Id: 2, Threads: []int{5, 11}},
   442  						},
   443  					},
   444  				},
   445  			},
   446  			want: &CPUTopology{
   447  				NumCPUs:      12,
   448  				NumSockets:   2,
   449  				NumCores:     6,
   450  				NumNUMANodes: 2,
   451  				CPUDetails: map[int]CPUInfo{
   452  					0:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   453  					1:  {CoreID: 1, SocketID: 0, NUMANodeID: 0},
   454  					2:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   455  					3:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
   456  					4:  {CoreID: 4, SocketID: 1, NUMANodeID: 1},
   457  					5:  {CoreID: 5, SocketID: 1, NUMANodeID: 1},
   458  					6:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   459  					7:  {CoreID: 1, SocketID: 0, NUMANodeID: 0},
   460  					8:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   461  					9:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
   462  					10: {CoreID: 4, SocketID: 1, NUMANodeID: 1},
   463  					11: {CoreID: 5, SocketID: 1, NUMANodeID: 1},
   464  				},
   465  			},
   466  			wantErr: false,
   467  		},
   468  		{
   469  			name: "OneSocketHT fail",
   470  			machineInfo: cadvisorapi.MachineInfo{
   471  				NumCores:   8,
   472  				NumSockets: 1,
   473  				Topology: []cadvisorapi.Node{
   474  					{Id: 0,
   475  						Cores: []cadvisorapi.Core{
   476  							{SocketID: 0, Id: 0, Threads: []int{0, 4}},
   477  							{SocketID: 0, Id: 1, Threads: []int{1, 5}},
   478  							{SocketID: 0, Id: 2, Threads: []int{2, 2}}, // Wrong case - should fail here
   479  							{SocketID: 0, Id: 3, Threads: []int{3, 7}},
   480  						},
   481  					},
   482  				},
   483  			},
   484  			want:    &CPUTopology{},
   485  			wantErr: true,
   486  		},
   487  		{
   488  			name: "OneSocketHT fail",
   489  			machineInfo: cadvisorapi.MachineInfo{
   490  				NumCores:   8,
   491  				NumSockets: 1,
   492  				Topology: []cadvisorapi.Node{
   493  					{Id: 0,
   494  						Cores: []cadvisorapi.Core{
   495  							{SocketID: 0, Id: 0, Threads: []int{0, 4}},
   496  							{SocketID: 0, Id: 1, Threads: []int{1, 5}},
   497  							{SocketID: 0, Id: 2, Threads: []int{2, 6}},
   498  							{SocketID: 0, Id: 3, Threads: []int{}}, // Wrong case - should fail here
   499  						},
   500  					},
   501  				},
   502  			},
   503  			want:    &CPUTopology{},
   504  			wantErr: true,
   505  		},
   506  	}
   507  	for _, tt := range tests {
   508  		t.Run(tt.name, func(t *testing.T) {
   509  			got, err := Discover(&tt.machineInfo)
   510  			if err != nil {
   511  				if tt.wantErr {
   512  					t.Logf("Discover() expected error = %v", err)
   513  				} else {
   514  					t.Errorf("Discover() error = %v, wantErr %v", err, tt.wantErr)
   515  				}
   516  				return
   517  			}
   518  			if diff := cmp.Diff(got, tt.want); diff != "" {
   519  				t.Errorf("Discover() = %v, want %v diff=%s", got, tt.want, diff)
   520  			}
   521  		})
   522  	}
   523  }
   524  
   525  func TestCPUDetailsKeepOnly(t *testing.T) {
   526  
   527  	var details CPUDetails
   528  	details = map[int]CPUInfo{
   529  		0: {},
   530  		1: {},
   531  		2: {},
   532  	}
   533  
   534  	tests := []struct {
   535  		name string
   536  		cpus cpuset.CPUSet
   537  		want CPUDetails
   538  	}{{
   539  		name: "cpus is in CPUDetails.",
   540  		cpus: cpuset.New(0, 1),
   541  		want: map[int]CPUInfo{
   542  			0: {},
   543  			1: {},
   544  		},
   545  	}, {
   546  		name: "cpus is not in CPUDetails.",
   547  		cpus: cpuset.New(3),
   548  		want: CPUDetails{},
   549  	}}
   550  
   551  	for _, tt := range tests {
   552  		t.Run(tt.name, func(t *testing.T) {
   553  			got := details.KeepOnly(tt.cpus)
   554  			if !reflect.DeepEqual(got, tt.want) {
   555  				t.Errorf("KeepOnly() = %v, want %v", got, tt.want)
   556  			}
   557  		})
   558  	}
   559  }
   560  
   561  func TestCPUDetailsNUMANodes(t *testing.T) {
   562  
   563  	tests := []struct {
   564  		name    string
   565  		details CPUDetails
   566  		want    cpuset.CPUSet
   567  	}{{
   568  		name: "Get CPUset of NUMANode IDs",
   569  		details: map[int]CPUInfo{
   570  			0: {NUMANodeID: 0},
   571  			1: {NUMANodeID: 0},
   572  			2: {NUMANodeID: 1},
   573  			3: {NUMANodeID: 1},
   574  		},
   575  		want: cpuset.New(0, 1),
   576  	}}
   577  
   578  	for _, tt := range tests {
   579  		t.Run(tt.name, func(t *testing.T) {
   580  			got := tt.details.NUMANodes()
   581  			if !reflect.DeepEqual(got, tt.want) {
   582  				t.Errorf("NUMANodes() = %v, want %v", got, tt.want)
   583  			}
   584  		})
   585  	}
   586  }
   587  
   588  func TestCPUDetailsNUMANodesInSockets(t *testing.T) {
   589  
   590  	var details1 CPUDetails
   591  	details1 = map[int]CPUInfo{
   592  		0: {SocketID: 0, NUMANodeID: 0},
   593  		1: {SocketID: 1, NUMANodeID: 0},
   594  		2: {SocketID: 2, NUMANodeID: 1},
   595  		3: {SocketID: 3, NUMANodeID: 1},
   596  	}
   597  
   598  	// poorly designed mainboards
   599  	var details2 CPUDetails
   600  	details2 = map[int]CPUInfo{
   601  		0: {SocketID: 0, NUMANodeID: 0},
   602  		1: {SocketID: 0, NUMANodeID: 1},
   603  		2: {SocketID: 1, NUMANodeID: 2},
   604  		3: {SocketID: 1, NUMANodeID: 3},
   605  	}
   606  
   607  	tests := []struct {
   608  		name    string
   609  		details CPUDetails
   610  		ids     []int
   611  		want    cpuset.CPUSet
   612  	}{{
   613  		name:    "Socket IDs is in CPUDetails.",
   614  		details: details1,
   615  		ids:     []int{0, 1, 2},
   616  		want:    cpuset.New(0, 1),
   617  	}, {
   618  		name:    "Socket IDs is not in CPUDetails.",
   619  		details: details1,
   620  		ids:     []int{4},
   621  		want:    cpuset.New(),
   622  	}, {
   623  		name:    "Socket IDs is in CPUDetails. (poorly designed mainboards)",
   624  		details: details2,
   625  		ids:     []int{0},
   626  		want:    cpuset.New(0, 1),
   627  	}, {
   628  		name:    "Socket IDs is not in CPUDetails. (poorly designed mainboards)",
   629  		details: details2,
   630  		ids:     []int{3},
   631  		want:    cpuset.New(),
   632  	}}
   633  
   634  	for _, tt := range tests {
   635  		t.Run(tt.name, func(t *testing.T) {
   636  			got := tt.details.NUMANodesInSockets(tt.ids...)
   637  			if !reflect.DeepEqual(got, tt.want) {
   638  				t.Errorf("NUMANodesInSockets() = %v, want %v", got, tt.want)
   639  			}
   640  		})
   641  	}
   642  }
   643  
   644  func TestCPUDetailsSockets(t *testing.T) {
   645  
   646  	tests := []struct {
   647  		name    string
   648  		details CPUDetails
   649  		want    cpuset.CPUSet
   650  	}{{
   651  		name: "Get CPUset of Socket IDs",
   652  		details: map[int]CPUInfo{
   653  			0: {SocketID: 0},
   654  			1: {SocketID: 0},
   655  			2: {SocketID: 1},
   656  			3: {SocketID: 1},
   657  		},
   658  		want: cpuset.New(0, 1),
   659  	}}
   660  
   661  	for _, tt := range tests {
   662  		t.Run(tt.name, func(t *testing.T) {
   663  			got := tt.details.Sockets()
   664  			if !reflect.DeepEqual(got, tt.want) {
   665  				t.Errorf("Sockets() = %v, want %v", got, tt.want)
   666  			}
   667  		})
   668  	}
   669  }
   670  
   671  func TestCPUDetailsCPUsInSockets(t *testing.T) {
   672  
   673  	var details CPUDetails
   674  	details = map[int]CPUInfo{
   675  		0: {SocketID: 0},
   676  		1: {SocketID: 0},
   677  		2: {SocketID: 1},
   678  		3: {SocketID: 2},
   679  	}
   680  
   681  	tests := []struct {
   682  		name string
   683  		ids  []int
   684  		want cpuset.CPUSet
   685  	}{{
   686  		name: "Socket IDs is in CPUDetails.",
   687  		ids:  []int{0, 1},
   688  		want: cpuset.New(0, 1, 2),
   689  	}, {
   690  		name: "Socket IDs is not in CPUDetails.",
   691  		ids:  []int{3},
   692  		want: cpuset.New(),
   693  	}}
   694  
   695  	for _, tt := range tests {
   696  		t.Run(tt.name, func(t *testing.T) {
   697  			got := details.CPUsInSockets(tt.ids...)
   698  			if !reflect.DeepEqual(got, tt.want) {
   699  				t.Errorf("CPUsInSockets() = %v, want %v", got, tt.want)
   700  			}
   701  		})
   702  	}
   703  }
   704  
   705  func TestCPUDetailsSocketsInNUMANodes(t *testing.T) {
   706  
   707  	var details CPUDetails
   708  	details = map[int]CPUInfo{
   709  		0: {NUMANodeID: 0, SocketID: 0},
   710  		1: {NUMANodeID: 0, SocketID: 1},
   711  		2: {NUMANodeID: 1, SocketID: 2},
   712  		3: {NUMANodeID: 2, SocketID: 3},
   713  	}
   714  
   715  	tests := []struct {
   716  		name string
   717  		ids  []int
   718  		want cpuset.CPUSet
   719  	}{{
   720  		name: "NUMANodes IDs is in CPUDetails.",
   721  		ids:  []int{0, 1},
   722  		want: cpuset.New(0, 1, 2),
   723  	}, {
   724  		name: "NUMANodes IDs is not in CPUDetails.",
   725  		ids:  []int{3},
   726  		want: cpuset.New(),
   727  	}}
   728  
   729  	for _, tt := range tests {
   730  		t.Run(tt.name, func(t *testing.T) {
   731  			got := details.SocketsInNUMANodes(tt.ids...)
   732  			if !reflect.DeepEqual(got, tt.want) {
   733  				t.Errorf("SocketsInNUMANodes() = %v, want %v", got, tt.want)
   734  			}
   735  		})
   736  	}
   737  }
   738  
   739  func TestCPUDetailsCores(t *testing.T) {
   740  
   741  	tests := []struct {
   742  		name    string
   743  		details CPUDetails
   744  		want    cpuset.CPUSet
   745  	}{{
   746  		name: "Get CPUset of Cores",
   747  		details: map[int]CPUInfo{
   748  			0: {CoreID: 0},
   749  			1: {CoreID: 0},
   750  			2: {CoreID: 1},
   751  			3: {CoreID: 1},
   752  		},
   753  		want: cpuset.New(0, 1),
   754  	}}
   755  
   756  	for _, tt := range tests {
   757  		t.Run(tt.name, func(t *testing.T) {
   758  			got := tt.details.Cores()
   759  			if !reflect.DeepEqual(got, tt.want) {
   760  				t.Errorf("Cores() = %v, want %v", got, tt.want)
   761  			}
   762  		})
   763  	}
   764  }
   765  
   766  func TestCPUDetailsCoresInNUMANodes(t *testing.T) {
   767  
   768  	var details CPUDetails
   769  	details = map[int]CPUInfo{
   770  		0: {NUMANodeID: 0, CoreID: 0},
   771  		1: {NUMANodeID: 0, CoreID: 1},
   772  		2: {NUMANodeID: 1, CoreID: 2},
   773  		3: {NUMANodeID: 2, CoreID: 3},
   774  	}
   775  
   776  	tests := []struct {
   777  		name string
   778  		ids  []int
   779  		want cpuset.CPUSet
   780  	}{{
   781  		name: "NUMANodes IDs is in CPUDetails.",
   782  		ids:  []int{0, 1},
   783  		want: cpuset.New(0, 1, 2),
   784  	}, {
   785  		name: "NUMANodes IDs is not in CPUDetails.",
   786  		ids:  []int{3},
   787  		want: cpuset.New(),
   788  	}}
   789  
   790  	for _, tt := range tests {
   791  		t.Run(tt.name, func(t *testing.T) {
   792  			got := details.CoresInNUMANodes(tt.ids...)
   793  			if !reflect.DeepEqual(got, tt.want) {
   794  				t.Errorf("CoresInNUMANodes() = %v, want %v", got, tt.want)
   795  			}
   796  		})
   797  	}
   798  }
   799  
   800  func TestCPUDetailsCoresInSockets(t *testing.T) {
   801  
   802  	var details CPUDetails
   803  	details = map[int]CPUInfo{
   804  		0: {SocketID: 0, CoreID: 0},
   805  		1: {SocketID: 0, CoreID: 1},
   806  		2: {SocketID: 1, CoreID: 2},
   807  		3: {SocketID: 2, CoreID: 3},
   808  	}
   809  
   810  	tests := []struct {
   811  		name string
   812  		ids  []int
   813  		want cpuset.CPUSet
   814  	}{{
   815  		name: "Socket IDs is in CPUDetails.",
   816  		ids:  []int{0, 1},
   817  		want: cpuset.New(0, 1, 2),
   818  	}, {
   819  		name: "Socket IDs is not in CPUDetails.",
   820  		ids:  []int{3},
   821  		want: cpuset.New(),
   822  	}}
   823  
   824  	for _, tt := range tests {
   825  		t.Run(tt.name, func(t *testing.T) {
   826  			got := details.CoresInSockets(tt.ids...)
   827  			if !reflect.DeepEqual(got, tt.want) {
   828  				t.Errorf("CoresInSockets() = %v, want %v", got, tt.want)
   829  			}
   830  		})
   831  	}
   832  }
   833  
   834  func TestCPUDetailsCPUs(t *testing.T) {
   835  
   836  	tests := []struct {
   837  		name    string
   838  		details CPUDetails
   839  		want    cpuset.CPUSet
   840  	}{{
   841  		name: "Get CPUset of CPUs",
   842  		details: map[int]CPUInfo{
   843  			0: {},
   844  			1: {},
   845  		},
   846  		want: cpuset.New(0, 1),
   847  	}}
   848  
   849  	for _, tt := range tests {
   850  		t.Run(tt.name, func(t *testing.T) {
   851  			got := tt.details.CPUs()
   852  			if !reflect.DeepEqual(got, tt.want) {
   853  				t.Errorf("CPUs() = %v, want %v", got, tt.want)
   854  			}
   855  		})
   856  	}
   857  }
   858  
   859  func TestCPUDetailsCPUsInNUMANodes(t *testing.T) {
   860  
   861  	var details CPUDetails
   862  	details = map[int]CPUInfo{
   863  		0: {NUMANodeID: 0},
   864  		1: {NUMANodeID: 0},
   865  		2: {NUMANodeID: 1},
   866  		3: {NUMANodeID: 2},
   867  	}
   868  
   869  	tests := []struct {
   870  		name string
   871  		ids  []int
   872  		want cpuset.CPUSet
   873  	}{{
   874  		name: "NUMANode IDs is in CPUDetails.",
   875  		ids:  []int{0, 1},
   876  		want: cpuset.New(0, 1, 2),
   877  	}, {
   878  		name: "NUMANode IDs is not in CPUDetails.",
   879  		ids:  []int{3},
   880  		want: cpuset.New(),
   881  	}}
   882  
   883  	for _, tt := range tests {
   884  		t.Run(tt.name, func(t *testing.T) {
   885  			got := details.CPUsInNUMANodes(tt.ids...)
   886  			if !reflect.DeepEqual(got, tt.want) {
   887  				t.Errorf("CPUsInNUMANodes() = %v, want %v", got, tt.want)
   888  			}
   889  		})
   890  	}
   891  }
   892  
   893  func TestCPUDetailsCPUsInCores(t *testing.T) {
   894  
   895  	var details CPUDetails
   896  	details = map[int]CPUInfo{
   897  		0: {CoreID: 0},
   898  		1: {CoreID: 0},
   899  		2: {CoreID: 1},
   900  		3: {CoreID: 2},
   901  	}
   902  
   903  	tests := []struct {
   904  		name string
   905  		ids  []int
   906  		want cpuset.CPUSet
   907  	}{{
   908  		name: "Core IDs is in CPUDetails.",
   909  		ids:  []int{0, 1},
   910  		want: cpuset.New(0, 1, 2),
   911  	}, {
   912  		name: "Core IDs is not in CPUDetails.",
   913  		ids:  []int{3},
   914  		want: cpuset.New(),
   915  	}}
   916  
   917  	for _, tt := range tests {
   918  		t.Run(tt.name, func(t *testing.T) {
   919  			got := details.CPUsInCores(tt.ids...)
   920  			if !reflect.DeepEqual(got, tt.want) {
   921  				t.Errorf("CPUsInCores() = %v, want %v", got, tt.want)
   922  			}
   923  		})
   924  	}
   925  }
   926  
   927  func TestCPUCoreID(t *testing.T) {
   928  	topoDualSocketHT := &CPUTopology{
   929  		NumCPUs:    12,
   930  		NumSockets: 2,
   931  		NumCores:   6,
   932  		CPUDetails: map[int]CPUInfo{
   933  			0:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   934  			1:  {CoreID: 1, SocketID: 1, NUMANodeID: 1},
   935  			2:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   936  			3:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
   937  			4:  {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   938  			5:  {CoreID: 5, SocketID: 1, NUMANodeID: 1},
   939  			6:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   940  			7:  {CoreID: 1, SocketID: 1, NUMANodeID: 1},
   941  			8:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   942  			9:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
   943  			10: {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   944  			11: {CoreID: 5, SocketID: 1, NUMANodeID: 1},
   945  		},
   946  	}
   947  
   948  	tests := []struct {
   949  		name    string
   950  		topo    *CPUTopology
   951  		id      int
   952  		want    int
   953  		wantErr bool
   954  	}{{
   955  		name: "Known Core ID",
   956  		topo: topoDualSocketHT,
   957  		id:   2,
   958  		want: 2,
   959  	}, {
   960  		name: "Known Core ID (core sibling).",
   961  		topo: topoDualSocketHT,
   962  		id:   8,
   963  		want: 2,
   964  	}, {
   965  		name:    "Unknown Core ID.",
   966  		topo:    topoDualSocketHT,
   967  		id:      -2,
   968  		want:    -1,
   969  		wantErr: true,
   970  	}}
   971  
   972  	for _, tt := range tests {
   973  		t.Run(tt.name, func(t *testing.T) {
   974  			got, err := tt.topo.CPUCoreID(tt.id)
   975  			gotErr := (err != nil)
   976  			if gotErr != tt.wantErr {
   977  				t.Errorf("CPUCoreID() returned err %v, want %v", gotErr, tt.wantErr)
   978  			}
   979  			if got != tt.want {
   980  				t.Errorf("CPUCoreID() returned %v, want %v", got, tt.want)
   981  			}
   982  		})
   983  	}
   984  }
   985  
   986  func TestCPUSocketID(t *testing.T) {
   987  	topoDualSocketHT := &CPUTopology{
   988  		NumCPUs:    12,
   989  		NumSockets: 2,
   990  		NumCores:   6,
   991  		CPUDetails: map[int]CPUInfo{
   992  			0:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   993  			1:  {CoreID: 1, SocketID: 1, NUMANodeID: 1},
   994  			2:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
   995  			3:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
   996  			4:  {CoreID: 4, SocketID: 0, NUMANodeID: 0},
   997  			5:  {CoreID: 5, SocketID: 1, NUMANodeID: 1},
   998  			6:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
   999  			7:  {CoreID: 1, SocketID: 1, NUMANodeID: 1},
  1000  			8:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
  1001  			9:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
  1002  			10: {CoreID: 4, SocketID: 0, NUMANodeID: 0},
  1003  			11: {CoreID: 5, SocketID: 1, NUMANodeID: 1},
  1004  		},
  1005  	}
  1006  
  1007  	tests := []struct {
  1008  		name    string
  1009  		topo    *CPUTopology
  1010  		id      int
  1011  		want    int
  1012  		wantErr bool
  1013  	}{{
  1014  		name: "Known Core ID",
  1015  		topo: topoDualSocketHT,
  1016  		id:   3,
  1017  		want: 1,
  1018  	}, {
  1019  		name: "Known Core ID (core sibling).",
  1020  		topo: topoDualSocketHT,
  1021  		id:   9,
  1022  		want: 1,
  1023  	}, {
  1024  		name:    "Unknown Core ID.",
  1025  		topo:    topoDualSocketHT,
  1026  		id:      1000,
  1027  		want:    -1,
  1028  		wantErr: true,
  1029  	}}
  1030  
  1031  	for _, tt := range tests {
  1032  		t.Run(tt.name, func(t *testing.T) {
  1033  			got, err := tt.topo.CPUSocketID(tt.id)
  1034  			gotErr := (err != nil)
  1035  			if gotErr != tt.wantErr {
  1036  				t.Errorf("CPUSocketID() returned err %v, want %v", gotErr, tt.wantErr)
  1037  			}
  1038  			if got != tt.want {
  1039  				t.Errorf("CPUSocketID() returned %v, want %v", got, tt.want)
  1040  			}
  1041  		})
  1042  	}
  1043  }
  1044  
  1045  func TestCPUNUMANodeID(t *testing.T) {
  1046  	topoDualSocketHT := &CPUTopology{
  1047  		NumCPUs:    12,
  1048  		NumSockets: 2,
  1049  		NumCores:   6,
  1050  		CPUDetails: map[int]CPUInfo{
  1051  			0:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
  1052  			1:  {CoreID: 1, SocketID: 1, NUMANodeID: 1},
  1053  			2:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
  1054  			3:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
  1055  			4:  {CoreID: 4, SocketID: 0, NUMANodeID: 0},
  1056  			5:  {CoreID: 5, SocketID: 1, NUMANodeID: 1},
  1057  			6:  {CoreID: 0, SocketID: 0, NUMANodeID: 0},
  1058  			7:  {CoreID: 1, SocketID: 1, NUMANodeID: 1},
  1059  			8:  {CoreID: 2, SocketID: 0, NUMANodeID: 0},
  1060  			9:  {CoreID: 3, SocketID: 1, NUMANodeID: 1},
  1061  			10: {CoreID: 4, SocketID: 0, NUMANodeID: 0},
  1062  			11: {CoreID: 5, SocketID: 1, NUMANodeID: 1},
  1063  		},
  1064  	}
  1065  
  1066  	tests := []struct {
  1067  		name    string
  1068  		topo    *CPUTopology
  1069  		id      int
  1070  		want    int
  1071  		wantErr bool
  1072  	}{{
  1073  		name: "Known Core ID",
  1074  		topo: topoDualSocketHT,
  1075  		id:   0,
  1076  		want: 0,
  1077  	}, {
  1078  		name: "Known Core ID (core sibling).",
  1079  		topo: topoDualSocketHT,
  1080  		id:   6,
  1081  		want: 0,
  1082  	}, {
  1083  		name:    "Unknown Core ID.",
  1084  		topo:    topoDualSocketHT,
  1085  		id:      1000,
  1086  		want:    -1,
  1087  		wantErr: true,
  1088  	}}
  1089  
  1090  	for _, tt := range tests {
  1091  		t.Run(tt.name, func(t *testing.T) {
  1092  			got, err := tt.topo.CPUNUMANodeID(tt.id)
  1093  			gotErr := (err != nil)
  1094  			if gotErr != tt.wantErr {
  1095  				t.Errorf("CPUSocketID() returned err %v, want %v", gotErr, tt.wantErr)
  1096  			}
  1097  			if got != tt.want {
  1098  				t.Errorf("CPUSocketID() returned %v, want %v", got, tt.want)
  1099  			}
  1100  		})
  1101  	}
  1102  }