From 7872ec177575d7e9c82b6b21498e4d7c8b67d2c8 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 14 Apr 2025 14:11:14 +0200 Subject: [PATCH 1/9] log/logtest: Remove AssertRecordEqual --- log/logtest/assertions.go | 78 ---------------------------------- log/logtest/assertions_test.go | 34 --------------- 2 files changed, 112 deletions(-) delete mode 100644 log/logtest/assertions.go delete mode 100644 log/logtest/assertions_test.go diff --git a/log/logtest/assertions.go b/log/logtest/assertions.go deleted file mode 100644 index 9b6145fe1af..00000000000 --- a/log/logtest/assertions.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package logtest // import "go.opentelemetry.io/otel/log/logtest" - -import ( - "slices" - "testing" - - "go.opentelemetry.io/otel/log" -) - -// AssertRecordEqual compares two log records, and fails the test if they are -// not equal. -func AssertRecordEqual(t testing.TB, want, got log.Record) bool { - t.Helper() - - if want.EventName() != got.EventName() { - t.Errorf("EventName value is not equal:\nwant: %v\ngot: %v", want.EventName(), got.EventName()) - return false - } - if !want.Timestamp().Equal(got.Timestamp()) { - t.Errorf("Timestamp value is not equal:\nwant: %v\ngot: %v", want.Timestamp(), got.Timestamp()) - return false - } - if !want.ObservedTimestamp().Equal(got.ObservedTimestamp()) { - t.Errorf( - "ObservedTimestamp value is not equal:\nwant: %v\ngot: %v", - want.ObservedTimestamp(), - got.ObservedTimestamp(), - ) - return false - } - if want.Severity() != got.Severity() { - t.Errorf("Severity value is not equal:\nwant: %v\ngot: %v", want.Severity(), got.Severity()) - return false - } - if want.SeverityText() != got.SeverityText() { - t.Errorf("SeverityText value is not equal:\nwant: %v\ngot: %v", want.SeverityText(), got.SeverityText()) - return false - } - if !assertBody(t, want.Body(), got) { - return false - } - - var attrs []log.KeyValue - want.WalkAttributes(func(kv log.KeyValue) bool { - attrs = append(attrs, kv) - return true - }) - return assertAttributes(t, attrs, got) -} - -func assertBody(t testing.TB, want log.Value, r log.Record) bool { - t.Helper() - got := r.Body() - if !got.Equal(want) { - t.Errorf("Body value is not equal:\nwant: %v\ngot: %v", want, got) - return false - } - - return true -} - -func assertAttributes(t testing.TB, want []log.KeyValue, r log.Record) bool { - t.Helper() - var got []log.KeyValue - r.WalkAttributes(func(kv log.KeyValue) bool { - got = append(got, kv) - return true - }) - if !slices.EqualFunc(want, got, log.KeyValue.Equal) { - t.Errorf("Attributes are not equal:\nwant: %v\ngot: %v", want, got) - return false - } - - return true -} diff --git a/log/logtest/assertions_test.go b/log/logtest/assertions_test.go deleted file mode 100644 index 04d62d15d35..00000000000 --- a/log/logtest/assertions_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package logtest - -import ( - "testing" - "time" - - "go.opentelemetry.io/otel/log" -) - -func TestAssertRecord(t *testing.T) { - r1 := log.Record{} - r2 := log.Record{} - AssertRecordEqual(t, r1, r2) - - now := time.Now() - r1.SetEventName("my_event") - r2.SetEventName("my_event") - r1.SetTimestamp(now) - r2.SetTimestamp(now) - r1.SetObservedTimestamp(now) - r2.SetObservedTimestamp(now) - r1.SetSeverity(log.SeverityTrace1) - r2.SetSeverity(log.SeverityTrace1) - r1.SetSeverityText("trace") - r2.SetSeverityText("trace") - r1.SetBody(log.StringValue("log body")) - r2.SetBody(log.StringValue("log body")) - r1.AddAttributes(log.Bool("attr", true)) - r2.AddAttributes(log.Bool("attr", true)) - AssertRecordEqual(t, r1, r2) -} From 1d96eca34f9e945b03bdd91c8c50a15cc762fbb8 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 14 Apr 2025 14:11:34 +0200 Subject: [PATCH 2/9] log/logtest: Add AssertEqual --- log/logtest/assert.go | 61 ++++++++++++++++ log/logtest/assert_test.go | 146 +++++++++++++++++++++++++++++++++++++ log/logtest/go.mod | 1 + 3 files changed, 208 insertions(+) create mode 100644 log/logtest/assert.go create mode 100644 log/logtest/assert_test.go diff --git a/log/logtest/assert.go b/log/logtest/assert.go new file mode 100644 index 00000000000..1f0e805a84d --- /dev/null +++ b/log/logtest/assert.go @@ -0,0 +1,61 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package logtest // import "go.opentelemetry.io/otel/log/logtest" + +import ( + "context" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "go.opentelemetry.io/otel/log" +) + +// TestingT reports failure messages. +// [testing.T] implements this interface. +type TestingT interface { + Errorf(format string, args ...any) +} + +// AssertEqual asserts that the two concrete data-types from the logtest package are equal. +func AssertEqual[T Recording | Record](t TestingT, want, got T, opts ...AssertOption) bool { + if h, ok := t.(interface{ Helper() }); ok { + h.Helper() + } + + cmpOpts := []cmp.Option{ + cmp.Comparer(func(x, y context.Context) bool { return x == y }), // Compare context. + cmpopts.SortSlices(func(a, b log.KeyValue) bool { return a.Key < b.Key }), // Unordered compare of the key values. + cmpopts.EquateEmpty(), // Empty and nil collections are equal. + } + + cfg := newAssertConfig(opts) + if cfg.ignoreTimestamp { + cmpOpts = append(cmpOpts, cmpopts.IgnoreTypes(time.Time{})) // Ignore Timestamps. + } + + if diff := cmp.Diff(want, got, cmpOpts...); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + return false + } + return true +} + +type assertConfig struct { + ignoreTimestamp bool +} + +func newAssertConfig(opts []AssertOption) assertConfig { + var cfg assertConfig + for _, opt := range opts { + cfg = opt.apply(cfg) + } + return cfg +} + +// AssertOption allows for fine grain control over how AssertEqual operates. +type AssertOption interface { + apply(cfg assertConfig) assertConfig +} diff --git a/log/logtest/assert_test.go b/log/logtest/assert_test.go new file mode 100644 index 00000000000..71a64602166 --- /dev/null +++ b/log/logtest/assert_test.go @@ -0,0 +1,146 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package logtest + +import ( + "context" + "testing" + "time" + + "go.opentelemetry.io/otel/log" +) + +var y2k = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) + +type mockTestingT struct { + errors []string +} + +func (m *mockTestingT) Errorf(format string, args ...any) { + m.errors = append(m.errors, format) +} + +func TestAssertEqualRecording(t *testing.T) { + tests := []struct { + name string + a Recording + b Recording + opts []AssertOption + want bool + }{ + { + name: "equal recordings", + a: Recording{ + Scope{Name: t.Name()}: []Record{ + {Timestamp: y2k, Context: context.Background(), Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}}, + }, + }, + b: Recording{ + Scope{Name: t.Name()}: []Record{ + {Timestamp: y2k, Context: context.Background(), Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}}, + }, + }, + want: true, + }, + { + name: "different recordings", + a: Recording{ + Scope{Name: t.Name()}: []Record{ + {Attributes: []log.KeyValue{log.String("foo", "bar")}}, + }, + }, + b: Recording{ + Scope{Name: t.Name()}: []Record{ + {Attributes: []log.KeyValue{log.Int("n", 1)}}, + }, + }, + want: false, + }, + { + name: "equal empty scopes", + a: Recording{ + Scope{Name: t.Name()}: nil, + }, + b: Recording{ + Scope{Name: t.Name()}: []Record{}, + }, + want: true, + }, + { + name: "equal empty attributes", + a: Recording{ + Scope{Name: t.Name()}: []Record{ + {Body: log.StringValue("msg"), Attributes: []log.KeyValue{}}, + }, + }, + b: Recording{ + Scope{Name: t.Name()}: []Record{ + {Body: log.StringValue("msg"), Attributes: nil}, + }, + }, + want: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + mockT := &mockTestingT{} + result := AssertEqual(mockT, tc.a, tc.b, tc.opts...) + if result != tc.want { + t.Errorf("AssertEqual() = %v, want %v", result, tc.want) + } + if !tc.want && len(mockT.errors) == 0 { + t.Errorf("expected Errorf call but got none") + } + }) + } +} + +func TestAssertEqualRecord(t *testing.T) { + tests := []struct { + name string + a Record + b Record + opts []AssertOption + want bool + }{ + { + name: "equal records", + a: Record{ + Timestamp: y2k, + Context: context.Background(), + Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}, + }, + b: Record{ + Timestamp: y2k, + Context: context.Background(), + Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}, + }, + want: true, + }, + { + name: "different records", + a: Record{ + Attributes: []log.KeyValue{log.String("foo", "bar")}, + }, + b: Record{ + Attributes: []log.KeyValue{log.Int("n", 1)}, + }, + want: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + mockT := &mockTestingT{} + result := AssertEqual(mockT, tc.a, tc.b, tc.opts...) + if result != tc.want { + t.Errorf("AssertEqual() = %v, want %v", result, tc.want) + } + if !tc.want && len(mockT.errors) == 0 { + t.Errorf("expected Errorf call but got none") + } + }) + } +} diff --git a/log/logtest/go.mod b/log/logtest/go.mod index c7752c0574d..52ab175ae13 100644 --- a/log/logtest/go.mod +++ b/log/logtest/go.mod @@ -3,6 +3,7 @@ module go.opentelemetry.io/otel/log/logtest go 1.23.0 require ( + github.com/google/go-cmp v0.7.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/otel v1.35.0 go.opentelemetry.io/otel/log v0.11.0 From 32fdc15336041cd7e47629a602a61ad415588b46 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 14 Apr 2025 14:13:46 +0200 Subject: [PATCH 3/9] Add changelog entries --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 687e493e21c..6c82a27fa67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,13 +15,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm The package contains semantic conventions from the `v1.31.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/v1.31.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.30.0`(#6479) - Add `Recording`, `Scope`, and `Record` types in `go.opentelemetry.io/otel/log/logtest`. (#6507) +- Add `AssertEqual` function in `go.opentelemetry.io/otel/log/logtest`. (#6662) ### Removed - Drop support for [Go 1.22]. (#6381, #6418) - Remove `Resource` field from `EnabledParameters` in `go.opentelemetry.io/otel/sdk/log`. (#6494) -- Remove `RecordFactory` type from `go.opentelemetry.io/otel/log/logtest`. (#6492) +- Remove `RecordFactory` type from `go.opentelemetry.io/otel/log/logtest`. (#6492) - Remove `ScopeRecords`, `EmittedRecord`, and `RecordFactory` types from `go.opentelemetry.io/otel/log/logtest`. (#6507) +- Remove`AssertRecordEqual` function in `go.opentelemetry.io/otel/log/logtest`, use `AssertEqual` instead. (#6662) ### Changed From 67789a5bd07fa28703a3987810ebde9e080d6941 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 14 Apr 2025 14:14:01 +0200 Subject: [PATCH 4/9] Fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c82a27fa67..900a25414ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Remove `Resource` field from `EnabledParameters` in `go.opentelemetry.io/otel/sdk/log`. (#6494) - Remove `RecordFactory` type from `go.opentelemetry.io/otel/log/logtest`. (#6492) - Remove `ScopeRecords`, `EmittedRecord`, and `RecordFactory` types from `go.opentelemetry.io/otel/log/logtest`. (#6507) -- Remove`AssertRecordEqual` function in `go.opentelemetry.io/otel/log/logtest`, use `AssertEqual` instead. (#6662) +- Remove `AssertRecordEqual` function in `go.opentelemetry.io/otel/log/logtest`, use `AssertEqual` instead. (#6662) ### Changed From 24230c0faa37eea3ee28ac03180941989886cd6b Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 14 Apr 2025 14:19:54 +0200 Subject: [PATCH 5/9] fmt --- log/logtest/assert.go | 6 ++++-- log/logtest/assert_test.go | 12 ++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/log/logtest/assert.go b/log/logtest/assert.go index 1f0e805a84d..301192eb83f 100644 --- a/log/logtest/assert.go +++ b/log/logtest/assert.go @@ -26,8 +26,10 @@ func AssertEqual[T Recording | Record](t TestingT, want, got T, opts ...AssertOp } cmpOpts := []cmp.Option{ - cmp.Comparer(func(x, y context.Context) bool { return x == y }), // Compare context. - cmpopts.SortSlices(func(a, b log.KeyValue) bool { return a.Key < b.Key }), // Unordered compare of the key values. + cmp.Comparer(func(x, y context.Context) bool { return x == y }), // Compare context. + cmpopts.SortSlices( + func(a, b log.KeyValue) bool { return a.Key < b.Key }, + ), // Unordered compare of the key values. cmpopts.EquateEmpty(), // Empty and nil collections are equal. } diff --git a/log/logtest/assert_test.go b/log/logtest/assert_test.go index 71a64602166..8fc0862213c 100644 --- a/log/logtest/assert_test.go +++ b/log/logtest/assert_test.go @@ -33,12 +33,20 @@ func TestAssertEqualRecording(t *testing.T) { name: "equal recordings", a: Recording{ Scope{Name: t.Name()}: []Record{ - {Timestamp: y2k, Context: context.Background(), Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}}, + { + Timestamp: y2k, + Context: context.Background(), + Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}, + }, }, }, b: Recording{ Scope{Name: t.Name()}: []Record{ - {Timestamp: y2k, Context: context.Background(), Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}}, + { + Timestamp: y2k, + Context: context.Background(), + Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}, + }, }, }, want: true, From 2d4396f3c449bc0568b75bda0f30077cf9f1d307 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 May 2025 09:42:53 +0200 Subject: [PATCH 6/9] Change AssertEqual signature to accept *testing.T --- log/logtest/assert.go | 16 +++++++++++----- log/logtest/assert_test.go | 22 ++++++++++++++++++++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/log/logtest/assert.go b/log/logtest/assert.go index 301192eb83f..fcadb553bc9 100644 --- a/log/logtest/assert.go +++ b/log/logtest/assert.go @@ -5,6 +5,7 @@ package logtest // import "go.opentelemetry.io/otel/log/logtest" import ( "context" + "testing" "time" "github.com/google/go-cmp/cmp" @@ -13,14 +14,19 @@ import ( "go.opentelemetry.io/otel/log" ) -// TestingT reports failure messages. -// [testing.T] implements this interface. -type TestingT interface { +// AssertEqual asserts that the two concrete data-types from the logtest package are equal. +func AssertEqual[T Recording | Record](t *testing.T, want, got T, opts ...AssertOption) bool { + t.Helper() + return assertEqual(t, want, got, opts...) +} + +// testingT reports failure messages. +// *testing.T implements this interface. +type testingT interface { Errorf(format string, args ...any) } -// AssertEqual asserts that the two concrete data-types from the logtest package are equal. -func AssertEqual[T Recording | Record](t TestingT, want, got T, opts ...AssertOption) bool { +func assertEqual[T Recording | Record](t testingT, want, got T, opts ...AssertOption) bool { if h, ok := t.(interface{ Helper() }); ok { h.Helper() } diff --git a/log/logtest/assert_test.go b/log/logtest/assert_test.go index 8fc0862213c..e43ae187782 100644 --- a/log/logtest/assert_test.go +++ b/log/logtest/assert_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/log" ) @@ -21,6 +23,22 @@ func (m *mockTestingT) Errorf(format string, args ...any) { m.errors = append(m.errors, format) } +func TestAssertEqual(t *testing.T) { + a := Recording{ + Scope{Name: t.Name()}: []Record{ + {Body: log.StringValue("msg"), Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}}, + }, + } + b := Recording{ + Scope{Name: t.Name()}: []Record{ + {Body: log.StringValue("msg"), Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}}, + }, + } + + got := assertEqual(t, a, b) + assert.True(t, got, "expected recordings to be equal") +} + func TestAssertEqualRecording(t *testing.T) { tests := []struct { name string @@ -94,7 +112,7 @@ func TestAssertEqualRecording(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mockT := &mockTestingT{} - result := AssertEqual(mockT, tc.a, tc.b, tc.opts...) + result := assertEqual(mockT, tc.a, tc.b, tc.opts...) if result != tc.want { t.Errorf("AssertEqual() = %v, want %v", result, tc.want) } @@ -142,7 +160,7 @@ func TestAssertEqualRecord(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mockT := &mockTestingT{} - result := AssertEqual(mockT, tc.a, tc.b, tc.opts...) + result := assertEqual(mockT, tc.a, tc.b, tc.opts...) if result != tc.want { t.Errorf("AssertEqual() = %v, want %v", result, tc.want) } From 72717ea0a02749026bf8181e9ac8a9cf780eafbd Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 May 2025 09:44:28 +0200 Subject: [PATCH 7/9] Remove unused code --- log/logtest/assert.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/log/logtest/assert.go b/log/logtest/assert.go index fcadb553bc9..9889c493f2b 100644 --- a/log/logtest/assert.go +++ b/log/logtest/assert.go @@ -6,7 +6,6 @@ package logtest // import "go.opentelemetry.io/otel/log/logtest" import ( "context" "testing" - "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -39,11 +38,6 @@ func assertEqual[T Recording | Record](t testingT, want, got T, opts ...AssertOp cmpopts.EquateEmpty(), // Empty and nil collections are equal. } - cfg := newAssertConfig(opts) - if cfg.ignoreTimestamp { - cmpOpts = append(cmpOpts, cmpopts.IgnoreTypes(time.Time{})) // Ignore Timestamps. - } - if diff := cmp.Diff(want, got, cmpOpts...); diff != "" { t.Errorf("mismatch (-want +got):\n%s", diff) return false @@ -52,15 +46,6 @@ func assertEqual[T Recording | Record](t testingT, want, got T, opts ...AssertOp } type assertConfig struct { - ignoreTimestamp bool -} - -func newAssertConfig(opts []AssertOption) assertConfig { - var cfg assertConfig - for _, opt := range opts { - cfg = opt.apply(cfg) - } - return cfg } // AssertOption allows for fine grain control over how AssertEqual operates. From 7758e7c31bf7ca8c6ffef5b7c2a6fb62b20f9251 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 May 2025 09:45:27 +0200 Subject: [PATCH 8/9] Fix TestAssertEqual --- log/logtest/assert_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/logtest/assert_test.go b/log/logtest/assert_test.go index e43ae187782..bf3e4b499d1 100644 --- a/log/logtest/assert_test.go +++ b/log/logtest/assert_test.go @@ -35,7 +35,7 @@ func TestAssertEqual(t *testing.T) { }, } - got := assertEqual(t, a, b) + got := AssertEqual(t, a, b) assert.True(t, got, "expected recordings to be equal") } From e125c4b39448f239431e09944ae629fc2c1e8f10 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Thu, 8 May 2025 12:33:47 +0200 Subject: [PATCH 9/9] Fix lint error --- log/logtest/assert.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/log/logtest/assert.go b/log/logtest/assert.go index 9889c493f2b..859c170d94a 100644 --- a/log/logtest/assert.go +++ b/log/logtest/assert.go @@ -25,7 +25,7 @@ type testingT interface { Errorf(format string, args ...any) } -func assertEqual[T Recording | Record](t testingT, want, got T, opts ...AssertOption) bool { +func assertEqual[T Recording | Record](t testingT, want, got T, _ ...AssertOption) bool { if h, ok := t.(interface{ Helper() }); ok { h.Helper() } @@ -45,8 +45,7 @@ func assertEqual[T Recording | Record](t testingT, want, got T, opts ...AssertOp return true } -type assertConfig struct { -} +type assertConfig struct{} // AssertOption allows for fine grain control over how AssertEqual operates. type AssertOption interface {