230 lines
6.2 KiB
Go
230 lines
6.2 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package sharedcomponent
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/component/componentstatus"
|
|
"go.opentelemetry.io/collector/component/componenttest"
|
|
)
|
|
|
|
var id = component.MustNewID("test")
|
|
|
|
type baseComponent struct {
|
|
component.StartFunc
|
|
component.ShutdownFunc
|
|
telemetry *component.TelemetrySettings
|
|
}
|
|
|
|
func TestNewMap(t *testing.T) {
|
|
comps := NewMap[component.ID, *baseComponent]()
|
|
assert.Empty(t, comps.components)
|
|
}
|
|
|
|
func TestNewSharedComponentsCreateError(t *testing.T) {
|
|
comps := NewMap[component.ID, *baseComponent]()
|
|
assert.Empty(t, comps.components)
|
|
myErr := errors.New("my error")
|
|
_, err := comps.LoadOrStore(
|
|
id,
|
|
func() (*baseComponent, error) { return nil, myErr },
|
|
newNopTelemetrySettings(),
|
|
)
|
|
require.ErrorIs(t, err, myErr)
|
|
assert.Empty(t, comps.components)
|
|
}
|
|
|
|
func TestSharedComponentsLoadOrStore(t *testing.T) {
|
|
nop := &baseComponent{}
|
|
|
|
comps := NewMap[component.ID, *baseComponent]()
|
|
got, err := comps.LoadOrStore(
|
|
id,
|
|
func() (*baseComponent, error) { return nop, nil },
|
|
newNopTelemetrySettings(),
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Len(t, comps.components, 1)
|
|
assert.Same(t, nop, got.Unwrap())
|
|
gotSecond, err := comps.LoadOrStore(
|
|
id,
|
|
func() (*baseComponent, error) { panic("should not be called") },
|
|
newNopTelemetrySettings(),
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
assert.Same(t, got, gotSecond)
|
|
|
|
// Shutdown nop will remove
|
|
require.NoError(t, got.Shutdown(context.Background()))
|
|
assert.Empty(t, comps.components)
|
|
gotThird, err := comps.LoadOrStore(
|
|
id,
|
|
func() (*baseComponent, error) { return nop, nil },
|
|
newNopTelemetrySettings(),
|
|
)
|
|
require.NoError(t, err)
|
|
assert.NotSame(t, got, gotThird)
|
|
}
|
|
|
|
func TestSharedComponent(t *testing.T) {
|
|
wantErr := errors.New("my error")
|
|
calledStart := 0
|
|
calledStop := 0
|
|
comp := &baseComponent{
|
|
StartFunc: func(context.Context, component.Host) error {
|
|
calledStart++
|
|
return wantErr
|
|
},
|
|
ShutdownFunc: func(context.Context) error {
|
|
calledStop++
|
|
return wantErr
|
|
}}
|
|
|
|
comps := NewMap[component.ID, *baseComponent]()
|
|
got, err := comps.LoadOrStore(
|
|
id,
|
|
func() (*baseComponent, error) { return comp, nil },
|
|
newNopTelemetrySettings(),
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, wantErr, got.Start(context.Background(), componenttest.NewNopHost()))
|
|
assert.Equal(t, 1, calledStart)
|
|
// Second time is not called anymore.
|
|
require.NoError(t, got.Start(context.Background(), componenttest.NewNopHost()))
|
|
assert.Equal(t, 1, calledStart)
|
|
// first time, shutdown is called.
|
|
assert.Equal(t, wantErr, got.Shutdown(context.Background()))
|
|
assert.Equal(t, 1, calledStop)
|
|
// Second time is not called anymore.
|
|
require.NoError(t, got.Shutdown(context.Background()))
|
|
assert.Equal(t, 1, calledStop)
|
|
}
|
|
|
|
func TestReportStatusOnStartShutdown(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
startErr error
|
|
shutdownErr error
|
|
expectedStatuses []componentstatus.Status
|
|
expectedNumReporterInstances int
|
|
}{
|
|
{
|
|
name: "successful start/stop",
|
|
startErr: nil,
|
|
shutdownErr: nil,
|
|
expectedStatuses: []componentstatus.Status{
|
|
componentstatus.StatusStarting,
|
|
componentstatus.StatusOK,
|
|
componentstatus.StatusStopping,
|
|
componentstatus.StatusStopped,
|
|
},
|
|
expectedNumReporterInstances: 3,
|
|
},
|
|
{
|
|
name: "start error",
|
|
startErr: assert.AnError,
|
|
shutdownErr: nil,
|
|
expectedStatuses: []componentstatus.Status{
|
|
componentstatus.StatusStarting,
|
|
componentstatus.StatusPermanentError,
|
|
},
|
|
expectedNumReporterInstances: 1,
|
|
},
|
|
{
|
|
name: "shutdown error",
|
|
shutdownErr: assert.AnError,
|
|
expectedStatuses: []componentstatus.Status{
|
|
componentstatus.StatusStarting,
|
|
componentstatus.StatusOK,
|
|
componentstatus.StatusStopping,
|
|
componentstatus.StatusPermanentError,
|
|
},
|
|
expectedNumReporterInstances: 3,
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
reportedStatuses := make(map[*componentstatus.InstanceID][]componentstatus.Status)
|
|
newStatusFunc := func(id *componentstatus.InstanceID, ev *componentstatus.Event) {
|
|
reportedStatuses[id] = append(reportedStatuses[id], ev.Status())
|
|
}
|
|
base := &baseComponent{}
|
|
if tc.startErr != nil {
|
|
base.StartFunc = func(context.Context, component.Host) error {
|
|
return tc.startErr
|
|
}
|
|
}
|
|
if tc.shutdownErr != nil {
|
|
base.ShutdownFunc = func(context.Context) error {
|
|
return tc.shutdownErr
|
|
}
|
|
}
|
|
comps := NewMap[component.ID, *baseComponent]()
|
|
var comp *Component[*baseComponent]
|
|
var err error
|
|
for i := 0; i < 3; i++ {
|
|
telemetrySettings := newNopTelemetrySettings()
|
|
if i == 0 {
|
|
base.telemetry = telemetrySettings
|
|
}
|
|
comp, err = comps.LoadOrStore(
|
|
id,
|
|
func() (*baseComponent, error) { return base, nil },
|
|
telemetrySettings,
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
baseHost := componenttest.NewNopHost()
|
|
for i := 0; i < 3; i++ {
|
|
err = comp.Start(context.Background(), &testHost{Host: baseHost, InstanceID: &componentstatus.InstanceID{}, newStatusFunc: newStatusFunc})
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
require.Equal(t, tc.startErr, err)
|
|
|
|
if tc.startErr == nil {
|
|
comp.hostWrapper.Report(componentstatus.NewEvent(componentstatus.StatusOK))
|
|
|
|
err = comp.Shutdown(context.Background())
|
|
require.Equal(t, tc.shutdownErr, err)
|
|
}
|
|
|
|
require.Len(t, reportedStatuses, tc.expectedNumReporterInstances)
|
|
|
|
for _, actualStatuses := range reportedStatuses {
|
|
require.Equal(t, tc.expectedStatuses, actualStatuses)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// newNopTelemetrySettings streamlines getting a pointer to a NopTelemetrySettings
|
|
func newNopTelemetrySettings() *component.TelemetrySettings {
|
|
set := componenttest.NewNopTelemetrySettings()
|
|
return &set
|
|
}
|
|
|
|
var _ component.Host = (*testHost)(nil)
|
|
var _ componentstatus.Reporter = (*testHost)(nil)
|
|
|
|
type testHost struct {
|
|
component.Host
|
|
*componentstatus.InstanceID
|
|
newStatusFunc func(id *componentstatus.InstanceID, ev *componentstatus.Event)
|
|
}
|
|
|
|
func (h *testHost) Report(e *componentstatus.Event) {
|
|
h.newStatusFunc(h.InstanceID, e)
|
|
}
|