//go:build linux && amd64 package intel_pmu import ( "errors" "fmt" "testing" ia "github.com/intel/iaevents" "github.com/stretchr/testify/require" "github.com/influxdata/telegraf/testutil" ) func TestResolveEntities(t *testing.T) { errMock := errors.New("mock error") mLog := testutil.Logger{} mTransformer := &MockTransformer{} mResolver := &iaEntitiesResolver{transformer: mTransformer, log: mLog} type test struct { perfEvent *ia.PerfEvent options ia.Options event *eventWithQuals } t.Run("nil entities", func(t *testing.T) { err := mResolver.resolveEntities([]*coreEventEntity{nil}, nil) require.Error(t, err) require.Contains(t, err.Error(), "core entity is nil") err = mResolver.resolveEntities(nil, []*uncoreEventEntity{nil}) require.Error(t, err) require.Contains(t, err.Error(), "uncore entity is nil") }) t.Run("nil parsed events", func(t *testing.T) { mCoreEntity := &coreEventEntity{parsedEvents: []*eventWithQuals{nil, nil}} mUncoreEntity := &uncoreEventEntity{parsedEvents: []*eventWithQuals{nil, nil}} err := mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, nil) require.Error(t, err) require.Contains(t, err.Error(), "parsed core event is nil") err = mResolver.resolveEntities(nil, []*uncoreEventEntity{mUncoreEntity}) require.Error(t, err) require.Contains(t, err.Error(), "parsed uncore event is nil") }) t.Run("fail to resolve core events", func(t *testing.T) { name := "mock event 1" mCoreEntity := &coreEventEntity{parsedEvents: []*eventWithQuals{{name: name}}, allEvents: false} matcher := ia.NewNameMatcher(name) mTransformer.On("Transform", nil, matcher).Once().Return(nil, errMock) err := mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, nil) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to resolve core event %q", name)) mTransformer.AssertExpectations(t) }) t.Run("fail to resolve uncore events", func(t *testing.T) { name := "mock event 1" mUncoreEntity := &uncoreEventEntity{parsedEvents: []*eventWithQuals{{name: name}}, allEvents: false} matcher := ia.NewNameMatcher(name) mTransformer.On("Transform", nil, matcher).Once().Return(nil, errMock) err := mResolver.resolveEntities(nil, []*uncoreEventEntity{mUncoreEntity}) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to resolve uncore event %q", name)) mTransformer.AssertExpectations(t) }) t.Run("resolve all core and uncore events", func(t *testing.T) { mCoreEntity := &coreEventEntity{allEvents: true} mUncoreEntity := &uncoreEventEntity{allEvents: true} corePerfEvents := []*ia.PerfEvent{ {Name: "core event1"}, {Name: "core event2"}, {Name: "core event3"}, } uncorePerfEvents := []*ia.PerfEvent{ {Name: "uncore event1", Uncore: true}, {Name: "uncore event2", Uncore: true}, {Name: "uncore event3", Uncore: true}, } matcher := ia.NewNameMatcher() t.Run("fail to resolve all core events", func(t *testing.T) { mTransformer.On("Transform", nil, matcher).Once().Return(nil, errMock) err := mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, nil) require.Error(t, err) require.Contains(t, err.Error(), "failed to resolve all events") mTransformer.AssertExpectations(t) }) t.Run("fail to resolve all uncore events", func(t *testing.T) { mTransformer.On("Transform", nil, matcher).Once().Return(nil, errMock) err := mResolver.resolveEntities(nil, []*uncoreEventEntity{mUncoreEntity}) require.Error(t, err) require.Contains(t, err.Error(), "failed to resolve all events") mTransformer.AssertExpectations(t) }) t.Run("fail to resolve all events with transformationError", func(t *testing.T) { transformErr := &ia.TransformationError{} mTransformer.On("Transform", nil, matcher).Once().Return(corePerfEvents, transformErr).Once() mTransformer.On("Transform", nil, matcher).Once().Return(uncorePerfEvents, transformErr).Once() err := mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, []*uncoreEventEntity{mUncoreEntity}) require.NoError(t, err) require.Len(t, mCoreEntity.parsedEvents, len(corePerfEvents)) require.Len(t, mUncoreEntity.parsedEvents, len(uncorePerfEvents)) for _, coreEvent := range mCoreEntity.parsedEvents { require.Contains(t, corePerfEvents, coreEvent.custom.Event) } for _, uncoreEvent := range mUncoreEntity.parsedEvents { require.Contains(t, uncorePerfEvents, uncoreEvent.custom.Event) } mTransformer.AssertExpectations(t) }) mTransformer.On("Transform", nil, matcher).Once().Return(corePerfEvents, nil).Once() mTransformer.On("Transform", nil, matcher).Once().Return(uncorePerfEvents, nil).Once() err := mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, []*uncoreEventEntity{mUncoreEntity}) require.NoError(t, err) require.Len(t, mCoreEntity.parsedEvents, len(corePerfEvents)) require.Len(t, mUncoreEntity.parsedEvents, len(uncorePerfEvents)) for _, coreEvent := range mCoreEntity.parsedEvents { require.Contains(t, corePerfEvents, coreEvent.custom.Event) } for _, uncoreEvent := range mUncoreEntity.parsedEvents { require.Contains(t, uncorePerfEvents, uncoreEvent.custom.Event) } mTransformer.AssertExpectations(t) }) t.Run("uncore event found in core entity", func(t *testing.T) { mQuals := []string{"config1=0x23h"} eventName := "uncore event 1" testCase := test{ event: &eventWithQuals{name: eventName, qualifiers: mQuals}, perfEvent: &ia.PerfEvent{Name: eventName, Uncore: true}, } matcher := ia.NewNameMatcher(eventName) mTransformer.On("Transform", nil, matcher).Return([]*ia.PerfEvent{testCase.perfEvent}, nil).Once() mCoreEntity := &coreEventEntity{parsedEvents: []*eventWithQuals{testCase.event}, allEvents: false} err := mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, nil) require.ErrorContains(t, err, fmt.Sprintf("uncore event %q found in core entity", eventName)) mTransformer.AssertExpectations(t) }) t.Run("core event found in uncore entity", func(t *testing.T) { mQuals := []string{"config1=0x23h"} eventName := "core event 1" testCase := test{ event: &eventWithQuals{name: eventName, qualifiers: mQuals}, perfEvent: &ia.PerfEvent{Name: eventName, Uncore: false}, } matcher := ia.NewNameMatcher(eventName) mTransformer.On("Transform", nil, matcher).Return([]*ia.PerfEvent{testCase.perfEvent}, nil).Once() mUncoreEntity := &uncoreEventEntity{parsedEvents: []*eventWithQuals{testCase.event}, allEvents: false} err := mResolver.resolveEntities(nil, []*uncoreEventEntity{mUncoreEntity}) require.ErrorContains(t, err, fmt.Sprintf("core event %q found in uncore entity", eventName)) mTransformer.AssertExpectations(t) }) t.Run("resolve core and uncore events", func(t *testing.T) { var mCoreEvents []*eventWithQuals var nUncoreEvents []*eventWithQuals mQuals := []string{"config1=0x23h"} mOptions, err := ia.NewOptions().SetAttrModifiers(mQuals).Build() require.NoError(t, err) emptyOptions, err := ia.NewOptions().Build() require.NoError(t, err) coreTestCases := []test{ {event: &eventWithQuals{name: "core1", qualifiers: mQuals}, options: mOptions, perfEvent: &ia.PerfEvent{Name: "core1"}}, {event: &eventWithQuals{name: "core2", qualifiers: nil}, options: emptyOptions, perfEvent: &ia.PerfEvent{Name: "core2"}}, {event: &eventWithQuals{name: "core3", qualifiers: nil}, options: emptyOptions, perfEvent: &ia.PerfEvent{Name: "core3"}}, } uncoreTestCases := []test{ {event: &eventWithQuals{name: "uncore1", qualifiers: mQuals}, options: mOptions, perfEvent: &ia.PerfEvent{Name: "uncore1", Uncore: true}}, {event: &eventWithQuals{name: "uncore2", qualifiers: nil}, options: emptyOptions, perfEvent: &ia.PerfEvent{Name: "uncore2", Uncore: true}}, {event: &eventWithQuals{name: "uncore3", qualifiers: nil}, options: emptyOptions, perfEvent: &ia.PerfEvent{Name: "uncore3", Uncore: true}}, } for _, test := range coreTestCases { matcher := ia.NewNameMatcher(test.event.name) mTransformer.On("Transform", nil, matcher).Return([]*ia.PerfEvent{test.perfEvent}, nil).Once() mCoreEvents = append(mCoreEvents, test.event) } for _, test := range uncoreTestCases { matcher := ia.NewNameMatcher(test.event.name) mTransformer.On("Transform", nil, matcher).Return([]*ia.PerfEvent{test.perfEvent}, nil).Once() nUncoreEvents = append(nUncoreEvents, test.event) } mCoreEntity := &coreEventEntity{parsedEvents: mCoreEvents, allEvents: false} mUncoreEntity := &uncoreEventEntity{parsedEvents: nUncoreEvents, allEvents: false} err = mResolver.resolveEntities([]*coreEventEntity{mCoreEntity}, []*uncoreEventEntity{mUncoreEntity}) require.NoError(t, err) for _, test := range append(coreTestCases, uncoreTestCases...) { require.Equal(t, test.perfEvent, test.event.custom.Event) require.Equal(t, test.options, test.event.custom.Options) } mTransformer.AssertExpectations(t) }) } func TestResolveAllEvents(t *testing.T) { mTransformer := &MockTransformer{} mResolver := &iaEntitiesResolver{transformer: mTransformer} t.Run("transformer is nil", func(t *testing.T) { mResolver := &iaEntitiesResolver{transformer: nil} _, _, err := mResolver.resolveAllEvents() require.Error(t, err) }) t.Run("transformer returns error", func(t *testing.T) { matcher := ia.NewNameMatcher() mTransformer.On("Transform", nil, matcher).Once().Return(nil, errors.New("mock error")) _, _, err := mResolver.resolveAllEvents() require.Error(t, err) mTransformer.AssertExpectations(t) }) t.Run("no events", func(t *testing.T) { matcher := ia.NewNameMatcher() mTransformer.On("Transform", nil, matcher).Once().Return(nil, nil) _, _, err := mResolver.resolveAllEvents() require.NoError(t, err) mTransformer.AssertExpectations(t) }) t.Run("successfully resolved events", func(t *testing.T) { perfEvent1 := &ia.PerfEvent{Name: "mock1"} perfEvent2 := &ia.PerfEvent{Name: "mock2"} uncorePerfEvent1 := &ia.PerfEvent{Name: "mock3", Uncore: true} uncorePerfEvent2 := &ia.PerfEvent{Name: "mock4", Uncore: true} options, err := ia.NewOptions().Build() require.NoError(t, err) perfEvents := []*ia.PerfEvent{perfEvent1, perfEvent2, uncorePerfEvent1, uncorePerfEvent2} expectedCore := []*eventWithQuals{ {name: perfEvent1.Name, custom: ia.CustomizableEvent{Event: perfEvent1, Options: options}}, {name: perfEvent2.Name, custom: ia.CustomizableEvent{Event: perfEvent2, Options: options}}, } expectedUncore := []*eventWithQuals{ {name: uncorePerfEvent1.Name, custom: ia.CustomizableEvent{Event: uncorePerfEvent1, Options: options}}, {name: uncorePerfEvent2.Name, custom: ia.CustomizableEvent{Event: uncorePerfEvent2, Options: options}}, } matcher := ia.NewNameMatcher() mTransformer.On("Transform", nil, matcher).Once().Return(perfEvents, nil) coreEvents, uncoreEvents, err := mResolver.resolveAllEvents() require.NoError(t, err) require.Equal(t, expectedCore, coreEvents) require.Equal(t, expectedUncore, uncoreEvents) mTransformer.AssertExpectations(t) }) } func TestResolveEvent(t *testing.T) { mTransformer := &MockTransformer{} mEvent := "mock event" mResolver := &iaEntitiesResolver{transformer: mTransformer} t.Run("transformer is nil", func(t *testing.T) { mResolver := &iaEntitiesResolver{transformer: nil} _, err := mResolver.resolveEvent("event", nil) require.Error(t, err) require.Contains(t, err.Error(), "events transformer is nil") }) t.Run("event is empty", func(t *testing.T) { _, err := mResolver.resolveEvent("", nil) require.Error(t, err) require.Contains(t, err.Error(), "event name is empty") }) t.Run("transformer returns error", func(t *testing.T) { matcher := ia.NewNameMatcher(mEvent) mTransformer.On("Transform", nil, matcher).Once().Return(nil, errors.New("mock error")) _, err := mResolver.resolveEvent(mEvent, nil) require.Error(t, err) require.Contains(t, err.Error(), "failed to transform perf events") mTransformer.AssertExpectations(t) }) t.Run("no events transformed", func(t *testing.T) { matcher := ia.NewNameMatcher(mEvent) mTransformer.On("Transform", nil, matcher).Once().Return(nil, nil) _, err := mResolver.resolveEvent(mEvent, nil) require.Error(t, err) require.Contains(t, err.Error(), "failed to resolve unknown event") mTransformer.AssertExpectations(t) }) t.Run("not valid qualifiers", func(t *testing.T) { event := "mock event 1" qualifiers := []string{"wrong modifiers"} matcher := ia.NewNameMatcher(event) mPerfEvent := &ia.PerfEvent{Name: event} mPerfEvents := []*ia.PerfEvent{mPerfEvent} mTransformer.On("Transform", nil, matcher).Once().Return(mPerfEvents, nil) _, err := mResolver.resolveEvent(event, qualifiers) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to build options for event %q", event)) mTransformer.AssertExpectations(t) }) t.Run("successfully transformed", func(t *testing.T) { event := "mock event 1" qualifiers := []string{"config1=0x012h", "config2=0x034k"} matcher := ia.NewNameMatcher(event) mPerfEvent := &ia.PerfEvent{Name: event} mPerfEvents := []*ia.PerfEvent{mPerfEvent} expectedOptions, err := ia.NewOptions().SetAttrModifiers(qualifiers).Build() require.NoError(t, err) mTransformer.On("Transform", nil, matcher).Once().Return(mPerfEvents, nil) customEvent, err := mResolver.resolveEvent(event, qualifiers) require.NoError(t, err) require.Equal(t, mPerfEvent, customEvent.Event) require.Equal(t, expectedOptions, customEvent.Options) mTransformer.AssertExpectations(t) }) }