//go:build linux && amd64 package intel_pmu import ( "errors" "fmt" "testing" ia "github.com/intel/iaevents" "github.com/stretchr/testify/require" ) type mockPlacementFactory struct { err bool } func (m *mockPlacementFactory) NewPlacements(_ string, cpu int, cpus ...int) ([]ia.PlacementProvider, error) { if m.err { return nil, errors.New("mock error") } placements := []ia.PlacementProvider{ &ia.Placement{CPU: cpu, PMUType: 4}, } for _, cpu := range cpus { placements = append(placements, &ia.Placement{CPU: cpu, PMUType: 4}) } return placements, nil } func TestActivateEntities(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{} // more core test cases in TestActivateCoreEvents t.Run("failed to activate core events", func(t *testing.T) { tag := "TAG" mEntities := []*coreEventEntity{{EventsTag: tag}} err := mEntitiesActivator.activateEntities(mEntities, nil) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to activate core events %q", tag)) }) // more uncore test cases in TestActivateUncoreEvents t.Run("failed to activate uncore events", func(t *testing.T) { tag := "TAG" mEntities := []*uncoreEventEntity{{EventsTag: tag}} err := mEntitiesActivator.activateEntities(nil, mEntities) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to activate uncore events %q", tag)) }) t.Run("nothing to do", func(t *testing.T) { err := mEntitiesActivator.activateEntities(nil, nil) require.NoError(t, err) }) } func TestActivateUncoreEvents(t *testing.T) { mActivator := &mockEventsActivator{} mMaker := &mockPlacementMaker{} errMock := errors.New("error mock") t.Run("entity is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} err := mEntitiesActivator.activateUncoreEvents(nil) require.Error(t, err) require.Contains(t, err.Error(), "uncore events entity is nil") }) t.Run("event is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} mEntity := &uncoreEventEntity{parsedEvents: []*eventWithQuals{nil, nil}} err := mEntitiesActivator.activateUncoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), "uncore parsed event is nil") }) t.Run("perf event is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} name := "event name" mEntity := &uncoreEventEntity{parsedEvents: []*eventWithQuals{{name: name, custom: ia.CustomizableEvent{Event: nil}}}} err := mEntitiesActivator.activateUncoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("perf event of %q event is nil", name)) }) t.Run("placement maker and perf activator is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: nil, perfActivator: nil} err := mEntitiesActivator.activateUncoreEvents(&uncoreEventEntity{}) require.Error(t, err) require.Contains(t, err.Error(), "events activator or placement maker is nil") }) t.Run("failed to create placements", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} eventName := "mock event 1" parsedEvents := []*eventWithQuals{{name: eventName, custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: eventName}}}} mEntity := &uncoreEventEntity{parsedEvents: parsedEvents, parsedSockets: []int{0, 1, 2}} mMaker.On("makeUncorePlacements", parsedEvents[0].custom.Event, mEntity.parsedSockets[0]).Return(nil, errMock).Once() err := mEntitiesActivator.activateUncoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("ailed to create uncore placements for event %q", eventName)) mMaker.AssertExpectations(t) }) t.Run("failed to activate event", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} eventName := "mock event 1" parsedEvents := []*eventWithQuals{{name: eventName, custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: eventName}}}} placements := []ia.PlacementProvider{&ia.Placement{CPU: 0}, &ia.Placement{CPU: 1}} mEntity := &uncoreEventEntity{parsedEvents: parsedEvents, parsedSockets: []int{0, 1, 2}} mMaker.On("makeUncorePlacements", parsedEvents[0].custom.Event, mEntity.parsedSockets[0]).Return(placements, nil).Once() mActivator.On("activateMulti", parsedEvents[0].custom.Event, placements, parsedEvents[0].custom.Options).Return(nil, errMock).Once() err := mEntitiesActivator.activateUncoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to activate multi event %q", eventName)) mMaker.AssertExpectations(t) mActivator.AssertExpectations(t) }) t.Run("successfully activate core events", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} parsedEvents := []*eventWithQuals{ {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 1", Uncore: true}}}, {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 2", Uncore: true}}}, {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 3", Uncore: true}}}, {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 4", Uncore: true}}}, } mEntity := &uncoreEventEntity{parsedEvents: parsedEvents, parsedSockets: []int{0, 1, 2}} placements := []ia.PlacementProvider{&ia.Placement{}, &ia.Placement{}, &ia.Placement{}} var expectedEvents []multiEvent for _, event := range parsedEvents { for _, socket := range mEntity.parsedSockets { mMaker.On("makeUncorePlacements", event.custom.Event, socket).Return(placements, nil).Once() newActiveMultiEvent := &ia.ActiveMultiEvent{} expectedEvents = append(expectedEvents, multiEvent{newActiveMultiEvent.Events(), event.custom.Event, socket}) mActivator.On("activateMulti", event.custom.Event, placements, event.custom.Options).Return(newActiveMultiEvent, nil).Once() } } err := mEntitiesActivator.activateUncoreEvents(mEntity) require.NoError(t, err) require.Equal(t, expectedEvents, mEntity.activeMultiEvents) mMaker.AssertExpectations(t) mActivator.AssertExpectations(t) }) } func TestActivateCoreEvents(t *testing.T) { mMaker := &mockPlacementMaker{} mActivator := &mockEventsActivator{} errMock := errors.New("error mock") t.Run("entity is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} err := mEntitiesActivator.activateCoreEvents(nil) require.Error(t, err) require.Contains(t, err.Error(), "core events entity is nil") }) t.Run("placement maker is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: nil, perfActivator: mActivator} err := mEntitiesActivator.activateCoreEvents(&coreEventEntity{}) require.Error(t, err) require.Contains(t, err.Error(), "placement maker is nil") }) t.Run("event is nil", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} mEntity := &coreEventEntity{parsedEvents: []*eventWithQuals{nil, nil}} err := mEntitiesActivator.activateCoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), "core parsed event is nil") }) t.Run("failed to create placements", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} parsedEvents := []*eventWithQuals{{name: "mock event 1", custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 1"}}}} mEntity := &coreEventEntity{PerfGroup: false, parsedEvents: parsedEvents, parsedCores: []int{0, 1, 2}} mMaker.On("makeCorePlacements", mEntity.parsedCores, parsedEvents[0].custom.Event).Return(nil, errMock).Once() err := mEntitiesActivator.activateCoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to create core placements for event %q", parsedEvents[0].name)) mMaker.AssertExpectations(t) }) t.Run("failed to activate event", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} parsedEvents := []*eventWithQuals{{name: "mock event 1", custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 1"}}}} placements := []ia.PlacementProvider{&ia.Placement{CPU: 0}, &ia.Placement{CPU: 1}} mEntity := &coreEventEntity{PerfGroup: false, parsedEvents: parsedEvents, parsedCores: []int{0, 1, 2}} event := parsedEvents[0] plc := placements[0] mMaker.On("makeCorePlacements", mEntity.parsedCores, event.custom.Event).Return(placements, nil).Once() mActivator.On("activateEvent", event.custom.Event, plc, event.custom.Options).Return(nil, errMock).Once() err := mEntitiesActivator.activateCoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to activate core event %q", parsedEvents[0].name)) mMaker.AssertExpectations(t) mActivator.AssertExpectations(t) }) t.Run("failed to activate core events group", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: nil} mEntity := &coreEventEntity{PerfGroup: true, parsedEvents: nil} err := mEntitiesActivator.activateCoreEvents(mEntity) require.Error(t, err) require.Contains(t, err.Error(), "failed to activate core events group") }) t.Run("successfully activate core events", func(t *testing.T) { mEntitiesActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} parsedEvents := []*eventWithQuals{ {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 1"}}}, {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 2"}}}, {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 3"}}}, {custom: ia.CustomizableEvent{Event: &ia.PerfEvent{Name: "mock event 4"}}}, } placements := []ia.PlacementProvider{&ia.Placement{CPU: 0}, &ia.Placement{CPU: 1}, &ia.Placement{CPU: 2}} mEntity := &coreEventEntity{PerfGroup: false, parsedEvents: parsedEvents, parsedCores: []int{0, 1, 2}} var activeEvents []*ia.ActiveEvent for _, event := range parsedEvents { mMaker.On("makeCorePlacements", mEntity.parsedCores, event.custom.Event).Return(placements, nil).Once() for _, plc := range placements { newActiveEvent := &ia.ActiveEvent{PerfEvent: event.custom.Event} activeEvents = append(activeEvents, newActiveEvent) mActivator.On("activateEvent", event.custom.Event, plc, event.custom.Options).Return(newActiveEvent, nil).Once() } } err := mEntitiesActivator.activateCoreEvents(mEntity) require.NoError(t, err) require.Equal(t, activeEvents, mEntity.activeEvents) mMaker.AssertExpectations(t) mActivator.AssertExpectations(t) }) } func TestActivateCoreEventsGroup(t *testing.T) { mMaker := &mockPlacementMaker{} mActivator := &mockEventsActivator{} eActivator := &iaEntitiesActivator{placementMaker: mMaker, perfActivator: mActivator} errMock := errors.New("mock error") leader := &ia.PerfEvent{Name: "mock event 1"} perfEvent2 := &ia.PerfEvent{Name: "mock event 2"} parsedEvents := []*eventWithQuals{{custom: ia.CustomizableEvent{Event: leader}}, {custom: ia.CustomizableEvent{Event: perfEvent2}}} placements := []ia.PlacementProvider{&ia.Placement{}, &ia.Placement{}} // cannot populate this struct due to unexported events field activeGroup := &ia.ActiveEventGroup{} mEntity := &coreEventEntity{ EventsTag: "mock group", PerfGroup: true, parsedEvents: parsedEvents, parsedCores: nil, } events := make([]ia.CustomizableEvent, 0, len(parsedEvents)) for _, event := range parsedEvents { events = append(events, event.custom) } t.Run("missing perf activator and placement maker", func(t *testing.T) { mActivator := &iaEntitiesActivator{} err := mActivator.activateCoreEventsGroup(nil) require.Error(t, err) require.Contains(t, err.Error(), "missing perf activator or placement maker") }) t.Run("missing parsed events", func(t *testing.T) { mActivator := &iaEntitiesActivator{placementMaker: &mockPlacementMaker{}, perfActivator: &mockEventsActivator{}} err := mActivator.activateCoreEventsGroup(nil) require.Error(t, err) require.Contains(t, err.Error(), "missing parsed events") }) t.Run("nil in parsed event", func(t *testing.T) { mEntity := &coreEventEntity{EventsTag: "Nice tag", PerfGroup: true, parsedEvents: []*eventWithQuals{nil, nil}} err := eActivator.activateCoreEventsGroup(mEntity) require.Error(t, err) require.Contains(t, err.Error(), "core event is nil") }) t.Run("failed to make core placements", func(t *testing.T) { mMaker.On("makeCorePlacements", mEntity.parsedCores, leader).Return(nil, errMock).Once() err := eActivator.activateCoreEventsGroup(mEntity) require.Error(t, err) require.Contains(t, err.Error(), "failed to make core placements") mMaker.AssertExpectations(t) }) t.Run("failed to activate group", func(t *testing.T) { mMaker.On("makeCorePlacements", mEntity.parsedCores, leader).Return(placements, nil).Once() mActivator.On("activateGroup", placements[0], events).Return(nil, errMock).Once() err := eActivator.activateCoreEventsGroup(mEntity) require.Error(t, err) require.Contains(t, err.Error(), errMock.Error()) mMaker.AssertExpectations(t) mActivator.AssertExpectations(t) }) var allActive []*ia.ActiveEvent t.Run("successfully activated group", func(t *testing.T) { mMaker.On("makeCorePlacements", mEntity.parsedCores, leader).Return(placements, nil).Once() for _, plc := range placements { mActivator.On("activateGroup", plc, events).Return(activeGroup, nil).Once() allActive = append(allActive, activeGroup.Events()...) } err := eActivator.activateCoreEventsGroup(mEntity) require.NoError(t, err) require.Equal(t, allActive, mEntity.activeEvents) mMaker.AssertExpectations(t) mActivator.AssertExpectations(t) }) } func TestMakeCorePlacements(t *testing.T) { tests := []struct { name string cores []int perfEvent ia.PlacementFactory result []ia.PlacementProvider errMsg string }{ {"no cores", nil, &ia.PerfEvent{}, nil, "no cores provided"}, {"one core placement", []int{1}, &mockPlacementFactory{}, []ia.PlacementProvider{&ia.Placement{CPU: 1, PMUType: 4}}, ""}, {"multiple core placement", []int{1, 2, 4}, &mockPlacementFactory{}, []ia.PlacementProvider{ &ia.Placement{CPU: 1, PMUType: 4}, &ia.Placement{CPU: 2, PMUType: 4}, &ia.Placement{CPU: 4, PMUType: 4}}, ""}, {"placement factory error", []int{1}, &mockPlacementFactory{true}, nil, "mock error"}, {"placement factory error 2", []int{1, 2, 3}, &mockPlacementFactory{true}, nil, "mock error"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { maker := &iaPlacementMaker{} providers, err := maker.makeCorePlacements(test.cores, test.perfEvent) if len(test.errMsg) > 0 { require.Error(t, err) require.Nil(t, providers) require.Contains(t, err.Error(), test.errMsg) return } require.NoError(t, err) require.Equal(t, test.result, providers) }) } } func TestActivateEventForPlacement(t *testing.T) { placement1 := &ia.Placement{CPU: 0} placement2 := &ia.Placement{CPU: 1} placement3 := &ia.Placement{CPU: 2} mPlacements := []ia.PlacementProvider{placement1, placement2, placement3} mPerfEvent := &ia.PerfEvent{Name: "mock1"} mOptions := &ia.PerfEventOptions{} mEvent := &eventWithQuals{name: mPerfEvent.Name, custom: ia.CustomizableEvent{Event: mPerfEvent, Options: mOptions}} mPerfActivator := &mockEventsActivator{} mActivator := &iaEntitiesActivator{perfActivator: mPerfActivator} t.Run("event is nil", func(t *testing.T) { activeEvents, err := mActivator.activateEventForPlacements(nil, mPlacements) require.Error(t, err) require.Contains(t, err.Error(), "core event is nil") require.Empty(t, activeEvents) }) t.Run("perf activator is nil", func(t *testing.T) { mActivator := &iaEntitiesActivator{} activeEvents, err := mActivator.activateEventForPlacements(mEvent, mPlacements) require.Error(t, err) require.Contains(t, err.Error(), "missing perf activator") require.Empty(t, activeEvents) }) t.Run("placements are nil", func(t *testing.T) { activeEvents, err := mActivator.activateEventForPlacements(mEvent, nil) require.NoError(t, err) require.Empty(t, activeEvents) }) t.Run("activation error", func(t *testing.T) { mPerfActivator.On("activateEvent", mPerfEvent, placement1, mOptions).Once().Return(nil, errors.New("err")) activeEvents, err := mActivator.activateEventForPlacements(mEvent, mPlacements) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to activate event %q", mEvent.name)) require.Nil(t, activeEvents) mPerfActivator.AssertExpectations(t) }) t.Run("successfully activated", func(t *testing.T) { mActiveEvent := &ia.ActiveEvent{} mActiveEvent2 := &ia.ActiveEvent{} mActiveEvent3 := &ia.ActiveEvent{} mPerfActivator.On("activateEvent", mPerfEvent, placement1, mOptions).Once().Return(mActiveEvent, nil). On("activateEvent", mPerfEvent, placement2, mOptions).Once().Return(mActiveEvent2, nil). On("activateEvent", mPerfEvent, placement3, mOptions).Once().Return(mActiveEvent3, nil) activeEvents, err := mActivator.activateEventForPlacements(mEvent, mPlacements) require.NoError(t, err) require.Len(t, activeEvents, len(mPlacements)) require.Contains(t, activeEvents, mActiveEvent) require.Contains(t, activeEvents, mActiveEvent2) mPerfActivator.AssertExpectations(t) }) }