From c513972985f40a3d0814b756e8edd2c3169178c5 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Thu, 10 Aug 2023 16:00:53 -0700 Subject: [PATCH] Generate internal/matchers (#4430) --- exporters/jaeger/internal/gen.go | 29 ++ exporters/jaeger/internal/internaltest/gen.go | 25 -- .../jaeger/internal/internaltest/harness.go | 2 +- .../jaeger/internal/matchers/expectation.go | 310 ++++++++++++++++++ .../jaeger/internal/matchers/expecter.go | 39 +++ .../internal/matchers/temporal_matcher.go | 28 ++ exporters/zipkin/internal/gen.go | 29 ++ exporters/zipkin/internal/internaltest/gen.go | 25 -- .../zipkin/internal/internaltest/harness.go | 2 +- .../zipkin/internal/matchers/expectation.go | 310 ++++++++++++++++++ .../zipkin/internal/matchers/expecter.go | 39 +++ .../internal/matchers/temporal_matcher.go | 28 ++ internal/gen.go | 29 ++ internal/internaltest/gen.go | 25 -- internal/matchers/expectation.go | 3 + internal/matchers/expecter.go | 3 + internal/matchers/temporal_matcher.go | 3 + internal/shared/internaltest/harness.go.tmpl | 2 +- internal/shared/matchers/expectation.go.tmpl | 310 ++++++++++++++++++ internal/shared/matchers/expecter.go.tmpl | 39 +++ .../shared/matchers/temporal_matcher.go.tmpl | 28 ++ sdk/internal/gen.go | 29 ++ sdk/internal/internaltest/gen.go | 25 -- sdk/internal/internaltest/harness.go | 2 +- sdk/internal/matchers/expectation.go | 310 ++++++++++++++++++ sdk/internal/matchers/expecter.go | 39 +++ sdk/internal/matchers/temporal_matcher.go | 28 ++ 27 files changed, 1637 insertions(+), 104 deletions(-) create mode 100644 exporters/jaeger/internal/gen.go delete mode 100644 exporters/jaeger/internal/internaltest/gen.go create mode 100644 exporters/jaeger/internal/matchers/expectation.go create mode 100644 exporters/jaeger/internal/matchers/expecter.go create mode 100644 exporters/jaeger/internal/matchers/temporal_matcher.go create mode 100644 exporters/zipkin/internal/gen.go delete mode 100644 exporters/zipkin/internal/internaltest/gen.go create mode 100644 exporters/zipkin/internal/matchers/expectation.go create mode 100644 exporters/zipkin/internal/matchers/expecter.go create mode 100644 exporters/zipkin/internal/matchers/temporal_matcher.go create mode 100644 internal/gen.go delete mode 100644 internal/internaltest/gen.go create mode 100644 internal/shared/matchers/expectation.go.tmpl create mode 100644 internal/shared/matchers/expecter.go.tmpl create mode 100644 internal/shared/matchers/temporal_matcher.go.tmpl create mode 100644 sdk/internal/gen.go delete mode 100644 sdk/internal/internaltest/gen.go create mode 100644 sdk/internal/matchers/expectation.go create mode 100644 sdk/internal/matchers/expecter.go create mode 100644 sdk/internal/matchers/temporal_matcher.go diff --git a/exporters/jaeger/internal/gen.go b/exporters/jaeger/internal/gen.go new file mode 100644 index 00000000000..ceb2b04e3d6 --- /dev/null +++ b/exporters/jaeger/internal/gen.go @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal // import "go.opentelemetry.io/otel/exporters/jaeger/internal" + +//go:generate gotmpl --body=../../../internal/shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go +//go:generate gotmpl --body=../../../internal/shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go +//go:generate gotmpl --body=../../../internal/shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go + +//go:generate gotmpl --body=../../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/exporters/jaeger/internal/matchers\"}" --out=internaltest/harness.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go diff --git a/exporters/jaeger/internal/internaltest/gen.go b/exporters/jaeger/internal/internaltest/gen.go deleted file mode 100644 index 3ee9f78234a..00000000000 --- a/exporters/jaeger/internal/internaltest/gen.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest" - -//go:generate gotmpl --body=../../../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=alignment.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=env.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=env_test.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=errors.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/harness.go.tmpl "--data={}" --out=harness.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=text_map_carrier.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=text_map_carrier_test.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=text_map_propagator.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=text_map_propagator_test.go diff --git a/exporters/jaeger/internal/internaltest/harness.go b/exporters/jaeger/internal/internaltest/harness.go index 3922726c72d..a938e733965 100644 --- a/exporters/jaeger/internal/internaltest/harness.go +++ b/exporters/jaeger/internal/internaltest/harness.go @@ -26,7 +26,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/internal/matchers" + "go.opentelemetry.io/otel/exporters/jaeger/internal/matchers" "go.opentelemetry.io/otel/trace" ) diff --git a/exporters/jaeger/internal/matchers/expectation.go b/exporters/jaeger/internal/matchers/expectation.go new file mode 100644 index 00000000000..8b1ab860867 --- /dev/null +++ b/exporters/jaeger/internal/matchers/expectation.go @@ -0,0 +1,310 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expectation.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/exporters/jaeger/internal/matchers" + +import ( + "fmt" + "reflect" + "regexp" + "runtime/debug" + "strings" + "testing" + "time" +) + +var ( + stackTracePruneRE = regexp.MustCompile(`runtime\/debug|testing|internal\/matchers`) +) + +type Expectation struct { + t *testing.T + actual interface{} +} + +func (e *Expectation) ToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if !reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) NotToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) ToBeNil() { + if e.actual != nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be nil", e.actual)) + } +} + +func (e *Expectation) NotToBeNil() { + if e.actual == nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to be nil", e.actual)) + } +} + +func (e *Expectation) ToBeTrue() { + switch a := e.actual.(type) { + case bool: + if !a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be true", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToBeFalse() { + switch a := e.actual.(type) { + case bool: + if a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be false", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) NotToPanic() { + switch a := e.actual.(type) { + case func(): + func() { + defer func() { + if recovered := recover(); recovered != nil { + e.fail(fmt.Sprintf("Expected panic\n\t%v\nto have not been raised", recovered)) + } + }() + + a() + }() + default: + e.fail(fmt.Sprintf("Cannot check if non-func value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToSucceed() { + switch actual := e.actual.(type) { + case error: + if actual != nil { + e.fail(fmt.Sprintf("Expected error\n\t%v\nto have succeeded", actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nsucceeded", actual)) + } +} + +func (e *Expectation) ToMatchError(expected interface{}) { + e.verifyExpectedNotNil(expected) + + actual, ok := e.actual.(error) + if !ok { + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nmatches error", e.actual)) + } + + switch expected := expected.(type) { + case error: + if !reflect.DeepEqual(actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + case string: + if actual.Error() != expected { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + default: + e.fail(fmt.Sprintf("Cannot match\n\t%v\nagainst non-error\n\t%v", actual, expected)) + } +} + +func (e *Expectation) ToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + var contained bool + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + contained = true + break + } + } + + if !contained { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain\n\t%v", e.actual, expectedElem)) + return + } + } +} + +func (e *Expectation) NotToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to contain\n\t%v", e.actual, expectedElem)) + return + } + } + } +} + +func (e *Expectation) ToMatchInAnyOrder(expected interface{}) { + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", expected)) + return + } + + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + if actualKind != expectedKind { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be the same type as\n\t%v", e.actual, expected)) + return + } + + if actualValue.Len() != expectedValue.Len() { + e.fail(fmt.Sprintf("Expected\n\t%v\nto have the same length as\n\t%v", e.actual, expected)) + return + } + + var unmatched []interface{} + + for i := 0; i < expectedValue.Len(); i++ { + unmatched = append(unmatched, expectedValue.Index(i).Interface()) + } + + for i := 0; i < actualValue.Len(); i++ { + var found bool + + for j, elem := range unmatched { + if reflect.DeepEqual(actualValue.Index(i).Interface(), elem) { + found = true + unmatched = append(unmatched[:j], unmatched[j+1:]...) + + break + } + } + + if !found { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain the same elements as\n\t%v", e.actual, expected)) + } + } +} + +func (e *Expectation) ToBeTemporally(matcher TemporalMatcher, compareTo interface{}) { + if actual, ok := e.actual.(time.Time); ok { + ct, ok := compareTo.(time.Time) + if !ok { + e.fail(fmt.Sprintf("Cannot compare to non-temporal value\n\t%v", compareTo)) + return + } + + switch matcher { + case Before: + if !actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before\n\t%v", e.actual, compareTo)) + } + case BeforeOrSameTime: + if actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before or at the same time as\n\t%v", e.actual, compareTo)) + } + case After: + if !actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after\n\t%v", e.actual, compareTo)) + } + case AfterOrSameTime: + if actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after or at the same time as\n\t%v", e.actual, compareTo)) + } + default: + e.fail("Cannot compare times with unexpected temporal matcher") + } + + return + } + + e.fail(fmt.Sprintf("Cannot compare non-temporal value\n\t%v", e.actual)) +} + +func (e *Expectation) verifyExpectedNotNil(expected interface{}) { + if expected == nil { + e.fail("Refusing to compare with . Use `ToBeNil` or `NotToBeNil` instead.") + } +} + +func (e *Expectation) fail(msg string) { + // Prune the stack trace so that it's easier to see relevant lines + stack := strings.Split(string(debug.Stack()), "\n") + var prunedStack []string + + for _, line := range stack { + if !stackTracePruneRE.MatchString(line) { + prunedStack = append(prunedStack, line) + } + } + + e.t.Fatalf("\n%s\n%s\n", strings.Join(prunedStack, "\n"), msg) +} diff --git a/exporters/jaeger/internal/matchers/expecter.go b/exporters/jaeger/internal/matchers/expecter.go new file mode 100644 index 00000000000..54f331025d8 --- /dev/null +++ b/exporters/jaeger/internal/matchers/expecter.go @@ -0,0 +1,39 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expecter.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/exporters/jaeger/internal/matchers" + +import ( + "testing" +) + +type Expecter struct { + t *testing.T +} + +func NewExpecter(t *testing.T) *Expecter { + return &Expecter{ + t: t, + } +} + +func (a *Expecter) Expect(actual interface{}) *Expectation { + return &Expectation{ + t: a.t, + actual: actual, + } +} diff --git a/exporters/jaeger/internal/matchers/temporal_matcher.go b/exporters/jaeger/internal/matchers/temporal_matcher.go new file mode 100644 index 00000000000..82871f71089 --- /dev/null +++ b/exporters/jaeger/internal/matchers/temporal_matcher.go @@ -0,0 +1,28 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/temporal_matcher.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/exporters/jaeger/internal/matchers" + +type TemporalMatcher byte + +//nolint:revive // ignoring missing comments for unexported constants in an internal package +const ( + Before TemporalMatcher = iota + BeforeOrSameTime + After + AfterOrSameTime +) diff --git a/exporters/zipkin/internal/gen.go b/exporters/zipkin/internal/gen.go new file mode 100644 index 00000000000..c686454cee4 --- /dev/null +++ b/exporters/zipkin/internal/gen.go @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal // import "go.opentelemetry.io/otel/exporters/zipkin/internal" + +//go:generate gotmpl --body=../../../internal/shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go +//go:generate gotmpl --body=../../../internal/shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go +//go:generate gotmpl --body=../../../internal/shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go + +//go:generate gotmpl --body=../../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/exporters/zipkin/internal/matchers\"}" --out=internaltest/harness.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go +//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go diff --git a/exporters/zipkin/internal/internaltest/gen.go b/exporters/zipkin/internal/internaltest/gen.go deleted file mode 100644 index c176d366b5f..00000000000 --- a/exporters/zipkin/internal/internaltest/gen.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest" - -//go:generate gotmpl --body=../../../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=alignment.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=env.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=env_test.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=errors.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/harness.go.tmpl "--data={}" --out=harness.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=text_map_carrier.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=text_map_carrier_test.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=text_map_propagator.go -//go:generate gotmpl --body=../../../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=text_map_propagator_test.go diff --git a/exporters/zipkin/internal/internaltest/harness.go b/exporters/zipkin/internal/internaltest/harness.go index 33b4f82307d..d8118d4f3b5 100644 --- a/exporters/zipkin/internal/internaltest/harness.go +++ b/exporters/zipkin/internal/internaltest/harness.go @@ -26,7 +26,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/internal/matchers" + "go.opentelemetry.io/otel/exporters/zipkin/internal/matchers" "go.opentelemetry.io/otel/trace" ) diff --git a/exporters/zipkin/internal/matchers/expectation.go b/exporters/zipkin/internal/matchers/expectation.go new file mode 100644 index 00000000000..411300d5819 --- /dev/null +++ b/exporters/zipkin/internal/matchers/expectation.go @@ -0,0 +1,310 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expectation.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/exporters/zipkin/internal/matchers" + +import ( + "fmt" + "reflect" + "regexp" + "runtime/debug" + "strings" + "testing" + "time" +) + +var ( + stackTracePruneRE = regexp.MustCompile(`runtime\/debug|testing|internal\/matchers`) +) + +type Expectation struct { + t *testing.T + actual interface{} +} + +func (e *Expectation) ToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if !reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) NotToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) ToBeNil() { + if e.actual != nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be nil", e.actual)) + } +} + +func (e *Expectation) NotToBeNil() { + if e.actual == nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to be nil", e.actual)) + } +} + +func (e *Expectation) ToBeTrue() { + switch a := e.actual.(type) { + case bool: + if !a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be true", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToBeFalse() { + switch a := e.actual.(type) { + case bool: + if a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be false", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) NotToPanic() { + switch a := e.actual.(type) { + case func(): + func() { + defer func() { + if recovered := recover(); recovered != nil { + e.fail(fmt.Sprintf("Expected panic\n\t%v\nto have not been raised", recovered)) + } + }() + + a() + }() + default: + e.fail(fmt.Sprintf("Cannot check if non-func value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToSucceed() { + switch actual := e.actual.(type) { + case error: + if actual != nil { + e.fail(fmt.Sprintf("Expected error\n\t%v\nto have succeeded", actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nsucceeded", actual)) + } +} + +func (e *Expectation) ToMatchError(expected interface{}) { + e.verifyExpectedNotNil(expected) + + actual, ok := e.actual.(error) + if !ok { + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nmatches error", e.actual)) + } + + switch expected := expected.(type) { + case error: + if !reflect.DeepEqual(actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + case string: + if actual.Error() != expected { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + default: + e.fail(fmt.Sprintf("Cannot match\n\t%v\nagainst non-error\n\t%v", actual, expected)) + } +} + +func (e *Expectation) ToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + var contained bool + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + contained = true + break + } + } + + if !contained { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain\n\t%v", e.actual, expectedElem)) + return + } + } +} + +func (e *Expectation) NotToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to contain\n\t%v", e.actual, expectedElem)) + return + } + } + } +} + +func (e *Expectation) ToMatchInAnyOrder(expected interface{}) { + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", expected)) + return + } + + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + if actualKind != expectedKind { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be the same type as\n\t%v", e.actual, expected)) + return + } + + if actualValue.Len() != expectedValue.Len() { + e.fail(fmt.Sprintf("Expected\n\t%v\nto have the same length as\n\t%v", e.actual, expected)) + return + } + + var unmatched []interface{} + + for i := 0; i < expectedValue.Len(); i++ { + unmatched = append(unmatched, expectedValue.Index(i).Interface()) + } + + for i := 0; i < actualValue.Len(); i++ { + var found bool + + for j, elem := range unmatched { + if reflect.DeepEqual(actualValue.Index(i).Interface(), elem) { + found = true + unmatched = append(unmatched[:j], unmatched[j+1:]...) + + break + } + } + + if !found { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain the same elements as\n\t%v", e.actual, expected)) + } + } +} + +func (e *Expectation) ToBeTemporally(matcher TemporalMatcher, compareTo interface{}) { + if actual, ok := e.actual.(time.Time); ok { + ct, ok := compareTo.(time.Time) + if !ok { + e.fail(fmt.Sprintf("Cannot compare to non-temporal value\n\t%v", compareTo)) + return + } + + switch matcher { + case Before: + if !actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before\n\t%v", e.actual, compareTo)) + } + case BeforeOrSameTime: + if actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before or at the same time as\n\t%v", e.actual, compareTo)) + } + case After: + if !actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after\n\t%v", e.actual, compareTo)) + } + case AfterOrSameTime: + if actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after or at the same time as\n\t%v", e.actual, compareTo)) + } + default: + e.fail("Cannot compare times with unexpected temporal matcher") + } + + return + } + + e.fail(fmt.Sprintf("Cannot compare non-temporal value\n\t%v", e.actual)) +} + +func (e *Expectation) verifyExpectedNotNil(expected interface{}) { + if expected == nil { + e.fail("Refusing to compare with . Use `ToBeNil` or `NotToBeNil` instead.") + } +} + +func (e *Expectation) fail(msg string) { + // Prune the stack trace so that it's easier to see relevant lines + stack := strings.Split(string(debug.Stack()), "\n") + var prunedStack []string + + for _, line := range stack { + if !stackTracePruneRE.MatchString(line) { + prunedStack = append(prunedStack, line) + } + } + + e.t.Fatalf("\n%s\n%s\n", strings.Join(prunedStack, "\n"), msg) +} diff --git a/exporters/zipkin/internal/matchers/expecter.go b/exporters/zipkin/internal/matchers/expecter.go new file mode 100644 index 00000000000..e29b15c7914 --- /dev/null +++ b/exporters/zipkin/internal/matchers/expecter.go @@ -0,0 +1,39 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expecter.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/exporters/zipkin/internal/matchers" + +import ( + "testing" +) + +type Expecter struct { + t *testing.T +} + +func NewExpecter(t *testing.T) *Expecter { + return &Expecter{ + t: t, + } +} + +func (a *Expecter) Expect(actual interface{}) *Expectation { + return &Expectation{ + t: a.t, + actual: actual, + } +} diff --git a/exporters/zipkin/internal/matchers/temporal_matcher.go b/exporters/zipkin/internal/matchers/temporal_matcher.go new file mode 100644 index 00000000000..dbd2690b4d6 --- /dev/null +++ b/exporters/zipkin/internal/matchers/temporal_matcher.go @@ -0,0 +1,28 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/temporal_matcher.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/exporters/zipkin/internal/matchers" + +type TemporalMatcher byte + +//nolint:revive // ignoring missing comments for unexported constants in an internal package +const ( + Before TemporalMatcher = iota + BeforeOrSameTime + After + AfterOrSameTime +) diff --git a/internal/gen.go b/internal/gen.go new file mode 100644 index 00000000000..f532f07e9e5 --- /dev/null +++ b/internal/gen.go @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal // import "go.opentelemetry.io/otel/internal" + +//go:generate gotmpl --body=./shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go +//go:generate gotmpl --body=./shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go +//go:generate gotmpl --body=./shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go + +//go:generate gotmpl --body=./shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go +//go:generate gotmpl --body=./shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go +//go:generate gotmpl --body=./shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go +//go:generate gotmpl --body=./shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go +//go:generate gotmpl --body=./shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/internal/matchers\"}" --out=internaltest/harness.go +//go:generate gotmpl --body=./shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go +//go:generate gotmpl --body=./shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go +//go:generate gotmpl --body=./shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go +//go:generate gotmpl --body=./shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go diff --git a/internal/internaltest/gen.go b/internal/internaltest/gen.go deleted file mode 100644 index 252143b5bee..00000000000 --- a/internal/internaltest/gen.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internaltest // import "go.opentelemetry.io/otel/internal/internaltest" - -//go:generate gotmpl --body=../shared/internaltest/alignment.go.tmpl "--data={}" --out=alignment.go -//go:generate gotmpl --body=../shared/internaltest/env.go.tmpl "--data={}" --out=env.go -//go:generate gotmpl --body=../shared/internaltest/env_test.go.tmpl "--data={}" --out=env_test.go -//go:generate gotmpl --body=../shared/internaltest/errors.go.tmpl "--data={}" --out=errors.go -//go:generate gotmpl --body=../shared/internaltest/harness.go.tmpl "--data={}" --out=harness.go -//go:generate gotmpl --body=../shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=text_map_carrier.go -//go:generate gotmpl --body=../shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=text_map_carrier_test.go -//go:generate gotmpl --body=../shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=text_map_propagator.go -//go:generate gotmpl --body=../shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=text_map_propagator_test.go diff --git a/internal/matchers/expectation.go b/internal/matchers/expectation.go index 0bf26266925..9cf408258b0 100644 --- a/internal/matchers/expectation.go +++ b/internal/matchers/expectation.go @@ -1,3 +1,6 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expectation.go.tmpl + // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/internal/matchers/expecter.go b/internal/matchers/expecter.go index 60221d5ec05..fc2ac0fb37e 100644 --- a/internal/matchers/expecter.go +++ b/internal/matchers/expecter.go @@ -1,3 +1,6 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expecter.go.tmpl + // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/internal/matchers/temporal_matcher.go b/internal/matchers/temporal_matcher.go index 75ed7dc0380..7e647a56722 100644 --- a/internal/matchers/temporal_matcher.go +++ b/internal/matchers/temporal_matcher.go @@ -1,3 +1,6 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/temporal_matcher.go.tmpl + // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/internal/shared/internaltest/harness.go.tmpl b/internal/shared/internaltest/harness.go.tmpl index ced44588894..da3e7f18615 100644 --- a/internal/shared/internaltest/harness.go.tmpl +++ b/internal/shared/internaltest/harness.go.tmpl @@ -26,7 +26,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/internal/matchers" + "{{ .matchersImportPath }}" "go.opentelemetry.io/otel/trace" ) diff --git a/internal/shared/matchers/expectation.go.tmpl b/internal/shared/matchers/expectation.go.tmpl new file mode 100644 index 00000000000..bdde84ea78a --- /dev/null +++ b/internal/shared/matchers/expectation.go.tmpl @@ -0,0 +1,310 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expectation.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers + +import ( + "fmt" + "reflect" + "regexp" + "runtime/debug" + "strings" + "testing" + "time" +) + +var ( + stackTracePruneRE = regexp.MustCompile(`runtime\/debug|testing|internal\/matchers`) +) + +type Expectation struct { + t *testing.T + actual interface{} +} + +func (e *Expectation) ToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if !reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) NotToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) ToBeNil() { + if e.actual != nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be nil", e.actual)) + } +} + +func (e *Expectation) NotToBeNil() { + if e.actual == nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to be nil", e.actual)) + } +} + +func (e *Expectation) ToBeTrue() { + switch a := e.actual.(type) { + case bool: + if !a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be true", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToBeFalse() { + switch a := e.actual.(type) { + case bool: + if a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be false", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) NotToPanic() { + switch a := e.actual.(type) { + case func(): + func() { + defer func() { + if recovered := recover(); recovered != nil { + e.fail(fmt.Sprintf("Expected panic\n\t%v\nto have not been raised", recovered)) + } + }() + + a() + }() + default: + e.fail(fmt.Sprintf("Cannot check if non-func value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToSucceed() { + switch actual := e.actual.(type) { + case error: + if actual != nil { + e.fail(fmt.Sprintf("Expected error\n\t%v\nto have succeeded", actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nsucceeded", actual)) + } +} + +func (e *Expectation) ToMatchError(expected interface{}) { + e.verifyExpectedNotNil(expected) + + actual, ok := e.actual.(error) + if !ok { + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nmatches error", e.actual)) + } + + switch expected := expected.(type) { + case error: + if !reflect.DeepEqual(actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + case string: + if actual.Error() != expected { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + default: + e.fail(fmt.Sprintf("Cannot match\n\t%v\nagainst non-error\n\t%v", actual, expected)) + } +} + +func (e *Expectation) ToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + var contained bool + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + contained = true + break + } + } + + if !contained { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain\n\t%v", e.actual, expectedElem)) + return + } + } +} + +func (e *Expectation) NotToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to contain\n\t%v", e.actual, expectedElem)) + return + } + } + } +} + +func (e *Expectation) ToMatchInAnyOrder(expected interface{}) { + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", expected)) + return + } + + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + if actualKind != expectedKind { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be the same type as\n\t%v", e.actual, expected)) + return + } + + if actualValue.Len() != expectedValue.Len() { + e.fail(fmt.Sprintf("Expected\n\t%v\nto have the same length as\n\t%v", e.actual, expected)) + return + } + + var unmatched []interface{} + + for i := 0; i < expectedValue.Len(); i++ { + unmatched = append(unmatched, expectedValue.Index(i).Interface()) + } + + for i := 0; i < actualValue.Len(); i++ { + var found bool + + for j, elem := range unmatched { + if reflect.DeepEqual(actualValue.Index(i).Interface(), elem) { + found = true + unmatched = append(unmatched[:j], unmatched[j+1:]...) + + break + } + } + + if !found { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain the same elements as\n\t%v", e.actual, expected)) + } + } +} + +func (e *Expectation) ToBeTemporally(matcher TemporalMatcher, compareTo interface{}) { + if actual, ok := e.actual.(time.Time); ok { + ct, ok := compareTo.(time.Time) + if !ok { + e.fail(fmt.Sprintf("Cannot compare to non-temporal value\n\t%v", compareTo)) + return + } + + switch matcher { + case Before: + if !actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before\n\t%v", e.actual, compareTo)) + } + case BeforeOrSameTime: + if actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before or at the same time as\n\t%v", e.actual, compareTo)) + } + case After: + if !actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after\n\t%v", e.actual, compareTo)) + } + case AfterOrSameTime: + if actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after or at the same time as\n\t%v", e.actual, compareTo)) + } + default: + e.fail("Cannot compare times with unexpected temporal matcher") + } + + return + } + + e.fail(fmt.Sprintf("Cannot compare non-temporal value\n\t%v", e.actual)) +} + +func (e *Expectation) verifyExpectedNotNil(expected interface{}) { + if expected == nil { + e.fail("Refusing to compare with . Use `ToBeNil` or `NotToBeNil` instead.") + } +} + +func (e *Expectation) fail(msg string) { + // Prune the stack trace so that it's easier to see relevant lines + stack := strings.Split(string(debug.Stack()), "\n") + var prunedStack []string + + for _, line := range stack { + if !stackTracePruneRE.MatchString(line) { + prunedStack = append(prunedStack, line) + } + } + + e.t.Fatalf("\n%s\n%s\n", strings.Join(prunedStack, "\n"), msg) +} diff --git a/internal/shared/matchers/expecter.go.tmpl b/internal/shared/matchers/expecter.go.tmpl new file mode 100644 index 00000000000..be06071e4bd --- /dev/null +++ b/internal/shared/matchers/expecter.go.tmpl @@ -0,0 +1,39 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expecter.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers + +import ( + "testing" +) + +type Expecter struct { + t *testing.T +} + +func NewExpecter(t *testing.T) *Expecter { + return &Expecter{ + t: t, + } +} + +func (a *Expecter) Expect(actual interface{}) *Expectation { + return &Expectation{ + t: a.t, + actual: actual, + } +} diff --git a/internal/shared/matchers/temporal_matcher.go.tmpl b/internal/shared/matchers/temporal_matcher.go.tmpl new file mode 100644 index 00000000000..270a3d84f99 --- /dev/null +++ b/internal/shared/matchers/temporal_matcher.go.tmpl @@ -0,0 +1,28 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/temporal_matcher.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers + +type TemporalMatcher byte + +//nolint:revive // ignoring missing comments for unexported constants in an internal package +const ( + Before TemporalMatcher = iota + BeforeOrSameTime + After + AfterOrSameTime +) diff --git a/sdk/internal/gen.go b/sdk/internal/gen.go new file mode 100644 index 00000000000..bd84f624b45 --- /dev/null +++ b/sdk/internal/gen.go @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal // import "go.opentelemetry.io/otel/sdk/internal" + +//go:generate gotmpl --body=../../internal/shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go +//go:generate gotmpl --body=../../internal/shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go +//go:generate gotmpl --body=../../internal/shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go + +//go:generate gotmpl --body=../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go +//go:generate gotmpl --body=../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go +//go:generate gotmpl --body=../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go +//go:generate gotmpl --body=../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go +//go:generate gotmpl --body=../../internal/shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/sdk/internal/matchers\"}" --out=internaltest/harness.go +//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go +//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go +//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go +//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go diff --git a/sdk/internal/internaltest/gen.go b/sdk/internal/internaltest/gen.go deleted file mode 100644 index a518c232c0d..00000000000 --- a/sdk/internal/internaltest/gen.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest" - -//go:generate gotmpl --body=../../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=alignment.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=env.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=env_test.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=errors.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/harness.go.tmpl "--data={}" --out=harness.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=text_map_carrier.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=text_map_carrier_test.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=text_map_propagator.go -//go:generate gotmpl --body=../../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=text_map_propagator_test.go diff --git a/sdk/internal/internaltest/harness.go b/sdk/internal/internaltest/harness.go index cd0207ca198..f177b0567a9 100644 --- a/sdk/internal/internaltest/harness.go +++ b/sdk/internal/internaltest/harness.go @@ -26,7 +26,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/internal/matchers" + "go.opentelemetry.io/otel/sdk/internal/matchers" "go.opentelemetry.io/otel/trace" ) diff --git a/sdk/internal/matchers/expectation.go b/sdk/internal/matchers/expectation.go new file mode 100644 index 00000000000..84764308651 --- /dev/null +++ b/sdk/internal/matchers/expectation.go @@ -0,0 +1,310 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expectation.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/sdk/internal/matchers" + +import ( + "fmt" + "reflect" + "regexp" + "runtime/debug" + "strings" + "testing" + "time" +) + +var ( + stackTracePruneRE = regexp.MustCompile(`runtime\/debug|testing|internal\/matchers`) +) + +type Expectation struct { + t *testing.T + actual interface{} +} + +func (e *Expectation) ToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if !reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) NotToEqual(expected interface{}) { + e.verifyExpectedNotNil(expected) + + if reflect.DeepEqual(e.actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to equal\n\t%v", e.actual, expected)) + } +} + +func (e *Expectation) ToBeNil() { + if e.actual != nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be nil", e.actual)) + } +} + +func (e *Expectation) NotToBeNil() { + if e.actual == nil { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to be nil", e.actual)) + } +} + +func (e *Expectation) ToBeTrue() { + switch a := e.actual.(type) { + case bool: + if !a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be true", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToBeFalse() { + switch a := e.actual.(type) { + case bool: + if a { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be false", e.actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) NotToPanic() { + switch a := e.actual.(type) { + case func(): + func() { + defer func() { + if recovered := recover(); recovered != nil { + e.fail(fmt.Sprintf("Expected panic\n\t%v\nto have not been raised", recovered)) + } + }() + + a() + }() + default: + e.fail(fmt.Sprintf("Cannot check if non-func value\n\t%v\nis truthy", a)) + } +} + +func (e *Expectation) ToSucceed() { + switch actual := e.actual.(type) { + case error: + if actual != nil { + e.fail(fmt.Sprintf("Expected error\n\t%v\nto have succeeded", actual)) + } + default: + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nsucceeded", actual)) + } +} + +func (e *Expectation) ToMatchError(expected interface{}) { + e.verifyExpectedNotNil(expected) + + actual, ok := e.actual.(error) + if !ok { + e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nmatches error", e.actual)) + } + + switch expected := expected.(type) { + case error: + if !reflect.DeepEqual(actual, expected) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + case string: + if actual.Error() != expected { + e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) + } + default: + e.fail(fmt.Sprintf("Cannot match\n\t%v\nagainst non-error\n\t%v", actual, expected)) + } +} + +func (e *Expectation) ToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + var contained bool + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + contained = true + break + } + } + + if !contained { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain\n\t%v", e.actual, expectedElem)) + return + } + } +} + +func (e *Expectation) NotToContain(expected interface{}) { + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + switch actualKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) + return + } + + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + expectedValue = reflect.ValueOf([]interface{}{expected}) + } + + for i := 0; i < expectedValue.Len(); i++ { + expectedElem := expectedValue.Index(i).Interface() + + for j := 0; j < actualValue.Len(); j++ { + if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { + e.fail(fmt.Sprintf("Expected\n\t%v\nnot to contain\n\t%v", e.actual, expectedElem)) + return + } + } + } +} + +func (e *Expectation) ToMatchInAnyOrder(expected interface{}) { + expectedValue := reflect.ValueOf(expected) + expectedKind := expectedValue.Kind() + + switch expectedKind { + case reflect.Array, reflect.Slice: + default: + e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", expected)) + return + } + + actualValue := reflect.ValueOf(e.actual) + actualKind := actualValue.Kind() + + if actualKind != expectedKind { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be the same type as\n\t%v", e.actual, expected)) + return + } + + if actualValue.Len() != expectedValue.Len() { + e.fail(fmt.Sprintf("Expected\n\t%v\nto have the same length as\n\t%v", e.actual, expected)) + return + } + + var unmatched []interface{} + + for i := 0; i < expectedValue.Len(); i++ { + unmatched = append(unmatched, expectedValue.Index(i).Interface()) + } + + for i := 0; i < actualValue.Len(); i++ { + var found bool + + for j, elem := range unmatched { + if reflect.DeepEqual(actualValue.Index(i).Interface(), elem) { + found = true + unmatched = append(unmatched[:j], unmatched[j+1:]...) + + break + } + } + + if !found { + e.fail(fmt.Sprintf("Expected\n\t%v\nto contain the same elements as\n\t%v", e.actual, expected)) + } + } +} + +func (e *Expectation) ToBeTemporally(matcher TemporalMatcher, compareTo interface{}) { + if actual, ok := e.actual.(time.Time); ok { + ct, ok := compareTo.(time.Time) + if !ok { + e.fail(fmt.Sprintf("Cannot compare to non-temporal value\n\t%v", compareTo)) + return + } + + switch matcher { + case Before: + if !actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before\n\t%v", e.actual, compareTo)) + } + case BeforeOrSameTime: + if actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before or at the same time as\n\t%v", e.actual, compareTo)) + } + case After: + if !actual.After(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after\n\t%v", e.actual, compareTo)) + } + case AfterOrSameTime: + if actual.Before(ct) { + e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after or at the same time as\n\t%v", e.actual, compareTo)) + } + default: + e.fail("Cannot compare times with unexpected temporal matcher") + } + + return + } + + e.fail(fmt.Sprintf("Cannot compare non-temporal value\n\t%v", e.actual)) +} + +func (e *Expectation) verifyExpectedNotNil(expected interface{}) { + if expected == nil { + e.fail("Refusing to compare with . Use `ToBeNil` or `NotToBeNil` instead.") + } +} + +func (e *Expectation) fail(msg string) { + // Prune the stack trace so that it's easier to see relevant lines + stack := strings.Split(string(debug.Stack()), "\n") + var prunedStack []string + + for _, line := range stack { + if !stackTracePruneRE.MatchString(line) { + prunedStack = append(prunedStack, line) + } + } + + e.t.Fatalf("\n%s\n%s\n", strings.Join(prunedStack, "\n"), msg) +} diff --git a/sdk/internal/matchers/expecter.go b/sdk/internal/matchers/expecter.go new file mode 100644 index 00000000000..089b6cf6d4b --- /dev/null +++ b/sdk/internal/matchers/expecter.go @@ -0,0 +1,39 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/expecter.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/sdk/internal/matchers" + +import ( + "testing" +) + +type Expecter struct { + t *testing.T +} + +func NewExpecter(t *testing.T) *Expecter { + return &Expecter{ + t: t, + } +} + +func (a *Expecter) Expect(actual interface{}) *Expectation { + return &Expectation{ + t: a.t, + actual: actual, + } +} diff --git a/sdk/internal/matchers/temporal_matcher.go b/sdk/internal/matchers/temporal_matcher.go new file mode 100644 index 00000000000..7703064aced --- /dev/null +++ b/sdk/internal/matchers/temporal_matcher.go @@ -0,0 +1,28 @@ +// Code created by gotmpl. DO NOT MODIFY. +// source: internal/shared/matchers/temporal_matcher.go.tmpl + +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matchers // import "go.opentelemetry.io/otel/sdk/internal/matchers" + +type TemporalMatcher byte + +//nolint:revive // ignoring missing comments for unexported constants in an internal package +const ( + Before TemporalMatcher = iota + BeforeOrSameTime + After + AfterOrSameTime +)