You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-06-23 00:07:52 +02:00
Decouple use of otel/internal/internaltest
(#4424)
* Add internaltest templates * Generate internaltest using gotmpl * Generate the sdk/internal/internaltest pkg * Use sdk/internal/internaltest in sdk module * Generate exporters/jaeger/internal/internaltest pkg * Use exporters/jaeger/internal/internaltest in jaeger exporter * Generate the exporters/zipkin/internal/internaltest pkg * Use local internaltest in zipkin exporter * Fix import path name in trace test
This commit is contained in:
@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewRawExporterWithDefault(t *testing.T) {
|
func TestNewRawExporterWithDefault(t *testing.T) {
|
||||||
|
74
exporters/jaeger/internal/internaltest/alignment.go
Normal file
74
exporters/jaeger/internal/internaltest/alignment.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/alignment.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 internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file contains common utilities and objects to validate memory alignment
|
||||||
|
of Go types. The primary use of this functionality is intended to ensure
|
||||||
|
`struct` fields that need to be 64-bit aligned so they can be passed as
|
||||||
|
arguments to 64-bit atomic operations.
|
||||||
|
|
||||||
|
The common workflow is to define a slice of `FieldOffset` and pass them to the
|
||||||
|
`Aligned8Byte` function from within a `TestMain` function from a package's
|
||||||
|
tests. It is important to make this call from the `TestMain` function prior
|
||||||
|
to running the rest of the test suit as it can provide useful diagnostics
|
||||||
|
about field alignment instead of ambiguous nil pointer dereference and runtime
|
||||||
|
panic.
|
||||||
|
|
||||||
|
For more information:
|
||||||
|
https://github.com/open-telemetry/opentelemetry-go/issues/341
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldOffset is a preprocessor representation of a struct field alignment.
|
||||||
|
type FieldOffset struct {
|
||||||
|
// Name of the field.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Offset of the field in bytes.
|
||||||
|
//
|
||||||
|
// To compute this at compile time use unsafe.Offsetof.
|
||||||
|
Offset uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligned8Byte returns if all fields are aligned modulo 8-bytes.
|
||||||
|
//
|
||||||
|
// Error messaging is printed to out for any field determined misaligned.
|
||||||
|
func Aligned8Byte(fields []FieldOffset, out io.Writer) bool {
|
||||||
|
misaligned := make([]FieldOffset, 0)
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Offset%8 != 0 {
|
||||||
|
misaligned = append(misaligned, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(misaligned) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(out, "struct fields not aligned for 64-bit atomic operations:")
|
||||||
|
for _, f := range misaligned {
|
||||||
|
fmt.Fprintf(out, " %s: %d-byte offset\n", f.Name, f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
101
exporters/jaeger/internal/internaltest/env.go
Normal file
101
exporters/jaeger/internal/internaltest/env.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env.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 internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Exists bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvStore stores and recovers environment variables.
|
||||||
|
type EnvStore interface {
|
||||||
|
// Records the environment variable into the store.
|
||||||
|
Record(key string)
|
||||||
|
|
||||||
|
// Restore recovers the environment variables in the store.
|
||||||
|
Restore() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ EnvStore = (*envStore)(nil)
|
||||||
|
|
||||||
|
type envStore struct {
|
||||||
|
store map[string]Env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) add(env Env) {
|
||||||
|
s.store[env.Name] = env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Restore() error {
|
||||||
|
var err error
|
||||||
|
for _, v := range s.store {
|
||||||
|
if v.Exists {
|
||||||
|
err = os.Setenv(v.Name, v.Value)
|
||||||
|
} else {
|
||||||
|
err = os.Unsetenv(v.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) setEnv(key, value string) error {
|
||||||
|
s.Record(key)
|
||||||
|
|
||||||
|
err := os.Setenv(key, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Record(key string) {
|
||||||
|
originValue, exists := os.LookupEnv(key)
|
||||||
|
s.add(Env{
|
||||||
|
Name: key,
|
||||||
|
Value: originValue,
|
||||||
|
Exists: exists,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnvStore() EnvStore {
|
||||||
|
return newEnvStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnvStore() *envStore {
|
||||||
|
return &envStore{store: make(map[string]Env)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetEnvVariables(env map[string]string) (EnvStore, error) {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
for k, v := range env {
|
||||||
|
err := envStore.setEnv(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envStore, nil
|
||||||
|
}
|
237
exporters/jaeger/internal/internaltest/env_test.go
Normal file
237
exporters/jaeger/internal/internaltest/env_test.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnvStoreTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_add() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
e := Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
}
|
||||||
|
envStore.add(e)
|
||||||
|
envStore.add(e)
|
||||||
|
|
||||||
|
s.Assert().Len(envStore.store, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRecord() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "record exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record exists env, but its value is empty",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record not exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Setenv(tc.env.Name, tc.env.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRestore() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvExists: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.add(tc.env)
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.Restore())
|
||||||
|
v, exists := os.LookupEnv(tc.env.Name)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_setEnv() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
key: "name",
|
||||||
|
value: "value",
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "other value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.key)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Setenv(tc.key, "other value"))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.setEnv(tc.key, tc.value))
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
v, exists := os.LookupEnv(tc.key)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvStoreTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(EnvStoreTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetEnvVariables(t *testing.T) {
|
||||||
|
envs := map[string]string{
|
||||||
|
"name1": "value1",
|
||||||
|
"name2": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
for k := range envs {
|
||||||
|
backup.Record(k)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, backup.Restore())
|
||||||
|
}()
|
||||||
|
|
||||||
|
store, err := SetEnvVariables(envs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.IsType(t, &envStore{}, store)
|
||||||
|
concreteStore := store.(*envStore)
|
||||||
|
assert.Len(t, concreteStore.store, 2)
|
||||||
|
assert.Equal(t, backup, concreteStore)
|
||||||
|
}
|
30
exporters/jaeger/internal/internaltest/errors.go
Normal file
30
exporters/jaeger/internal/internaltest/errors.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/errors.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 internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
|
|
||||||
|
type TestError string
|
||||||
|
|
||||||
|
var _ error = TestError("")
|
||||||
|
|
||||||
|
func NewTestError(s string) error {
|
||||||
|
return TestError(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TestError) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
25
exporters/jaeger/internal/internaltest/gen.go
Normal file
25
exporters/jaeger/internal/internaltest/gen.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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
|
344
exporters/jaeger/internal/internaltest/harness.go
Normal file
344
exporters/jaeger/internal/internaltest/harness.go
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/harness.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 internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/internal/matchers"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Harness is a testing harness used to test implementations of the
|
||||||
|
// OpenTelemetry API.
|
||||||
|
type Harness struct {
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHarness returns an instantiated *Harness using t.
|
||||||
|
func NewHarness(t *testing.T) *Harness {
|
||||||
|
return &Harness{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracerProvider runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// TracerProvider API.
|
||||||
|
func (h *Harness) TestTracerProvider(subjectFactory func() trace.TracerProvider) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("allow creating an arbitrary number of TracerProvider instances", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
e.Expect(tp1).NotToEqual(tp2)
|
||||||
|
})
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
runner := func(tp trace.TracerProvider) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.TracerProvider) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name, version string) {
|
||||||
|
_ = tp.Tracer(name, trace.WithInstrumentationVersion(version))
|
||||||
|
wg.Done()
|
||||||
|
}(fmt.Sprintf("tracer %d", i%5), fmt.Sprintf("%d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
matchers.NewExpecter(t).Expect(func() {
|
||||||
|
// Run with multiple TracerProvider to ensure they encapsulate
|
||||||
|
// their own Tracers.
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
done1 := runner(tp1)
|
||||||
|
done2 := runner(tp2)
|
||||||
|
|
||||||
|
<-done1
|
||||||
|
<-done2
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracer runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// Tracer API.
|
||||||
|
func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("propagates the original context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctxKey := testCtxKey{}
|
||||||
|
ctxValue := "ctx value"
|
||||||
|
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
|
||||||
|
|
||||||
|
ctx, _ = subject.Start(ctx, "test")
|
||||||
|
|
||||||
|
e.Expect(ctx.Value(ctxKey)).ToEqual(ctxValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("returns a span containing the expected properties", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
|
||||||
|
e.Expect(span.SpanContext().IsValid()).ToBeTrue()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stores the span on the provided context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
e.Expect(span.SpanContext()).NotToEqual(trace.SpanContext{})
|
||||||
|
e.Expect(trace.SpanFromContext(ctx)).ToEqual(span)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("starts spans with unique trace and span IDs", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span1 := subject.Start(context.Background(), "span1")
|
||||||
|
_, span2 := subject.Start(context.Background(), "span2")
|
||||||
|
|
||||||
|
sc1 := span1.SpanContext()
|
||||||
|
sc2 := span2.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID())
|
||||||
|
e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates a parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child")
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child")
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
tracer := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := tracer.Start(context.Background(), "span")
|
||||||
|
|
||||||
|
runner := func(tp trace.Tracer) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.Tracer) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name string) {
|
||||||
|
defer wg.Done()
|
||||||
|
_, child := tp.Start(ctx, name)
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
}(fmt.Sprintf("span %d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Expect(func() {
|
||||||
|
done := runner(tracer)
|
||||||
|
|
||||||
|
<-done
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
h.testSpan(subjectFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
|
||||||
|
var methods = map[string]func(span trace.Span){
|
||||||
|
"#End": func(span trace.Span) {
|
||||||
|
span.End()
|
||||||
|
},
|
||||||
|
"#AddEvent": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event")
|
||||||
|
},
|
||||||
|
"#AddEventWithTimestamp": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event", trace.WithTimestamp(time.Now().Add(1*time.Second)))
|
||||||
|
},
|
||||||
|
"#SetStatus": func(span trace.Span) {
|
||||||
|
span.SetStatus(codes.Error, "internal")
|
||||||
|
},
|
||||||
|
"#SetName": func(span trace.Span) {
|
||||||
|
span.SetName("new name")
|
||||||
|
},
|
||||||
|
"#SetAttributes": func(span trace.Span) {
|
||||||
|
span.SetAttributes(attribute.String("key1", "value"), attribute.Int("key2", 123))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var mechanisms = map[string]func() trace.Span{
|
||||||
|
"Span created via Tracer#Start": func() trace.Span {
|
||||||
|
tracer := tracerFactory()
|
||||||
|
_, subject := tracer.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
return subject
|
||||||
|
},
|
||||||
|
"Span created via span.TracerProvider()": func() trace.Span {
|
||||||
|
ctx, spanA := tracerFactory().Start(context.Background(), "span1")
|
||||||
|
|
||||||
|
_, spanB := spanA.TracerProvider().Tracer("second").Start(ctx, "span2")
|
||||||
|
return spanB
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for mechanismName, mechanism := range mechanisms {
|
||||||
|
h.t.Run(mechanismName, func(t *testing.T) {
|
||||||
|
for methodName, method := range methods {
|
||||||
|
t.Run(methodName, func(t *testing.T) {
|
||||||
|
t.Run("is thread-safe", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("#End", func(t *testing.T) {
|
||||||
|
t.Run("can be called multiple times", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
span.End()
|
||||||
|
span.End()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCtxKey struct{}
|
144
exporters/jaeger/internal/internaltest/text_map_carrier.go
Normal file
144
exporters/jaeger/internal/internaltest/text_map_carrier.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier.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 internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextMapCarrier is a storage medium for a TextMapPropagator used in testing.
|
||||||
|
// The methods of a TextMapCarrier are concurrent safe.
|
||||||
|
type TextMapCarrier struct {
|
||||||
|
mtx sync.Mutex
|
||||||
|
|
||||||
|
gets []string
|
||||||
|
sets [][2]string
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapCarrier = (*TextMapCarrier)(nil)
|
||||||
|
|
||||||
|
// NewTextMapCarrier returns a new *TextMapCarrier populated with data.
|
||||||
|
func NewTextMapCarrier(data map[string]string) *TextMapCarrier {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
return &TextMapCarrier{data: copied}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys for which this carrier has a value.
|
||||||
|
func (c *TextMapCarrier) Keys() []string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
result := make([]string, 0, len(c.data))
|
||||||
|
for k := range c.data {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value associated with the passed key.
|
||||||
|
func (c *TextMapCarrier) Get(key string) string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.gets = append(c.gets, key)
|
||||||
|
return c.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotKey tests if c.Get has been called for key.
|
||||||
|
func (c *TextMapCarrier) GotKey(t *testing.T, key string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
for _, k := range c.gets {
|
||||||
|
if k == key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) has not been called", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotN tests if n calls to c.Get have been made.
|
||||||
|
func (c *TextMapCarrier) GotN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.gets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Get was called %d times, not %d", len(c.gets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores the key-value pair.
|
||||||
|
func (c *TextMapCarrier) Set(key, value string) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.sets = append(c.sets, [2]string{key, value})
|
||||||
|
c.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeyValue tests if c.Set has been called for the key-value pair.
|
||||||
|
func (c *TextMapCarrier) SetKeyValue(t *testing.T, key, value string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
var vals []string
|
||||||
|
for _, pair := range c.sets {
|
||||||
|
if key == pair[0] {
|
||||||
|
if value == pair[1] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
vals = append(vals, pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(vals) > 0 {
|
||||||
|
t.Errorf("TextMapCarrier.Set called with %q and %v values, but not %s", key, vals, value)
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) has not been called", key, value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetN tests if n calls to c.Set have been made.
|
||||||
|
func (c *TextMapCarrier) SetN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.sets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Set was called %d times, not %d", len(c.sets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset zeros out the recording state and sets the carried values to data.
|
||||||
|
func (c *TextMapCarrier) Reset(data map[string]string) {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
c.gets = nil
|
||||||
|
c.sets = nil
|
||||||
|
c.data = copied
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, value = "test", "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapCarrierKeys(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
expected, actual := []string{key}, tmc.Keys()
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("expected tmc.Keys() to be %v but it was %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierGet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
if got := tmc.Get("empty"); got != "" {
|
||||||
|
t.Errorf("TextMapCarrier.Get returned %q for an empty key", got)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, "empty")
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
if got := tmc.Get(key); got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) returned %q, want %q", key, got, value)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.GotN(t, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierSet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(nil)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
if got, ok := tmc.data[key]; !ok {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) failed to store pair", key, value)
|
||||||
|
} else if got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) stored (%q,%q), not (%q,%q)", key, value, key, got, key, value)
|
||||||
|
}
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierReset(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear initial data")
|
||||||
|
}
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear data")
|
||||||
|
}
|
||||||
|
}
|
115
exporters/jaeger/internal/internaltest/text_map_propagator.go
Normal file
115
exporters/jaeger/internal/internaltest/text_map_propagator.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator.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 internaltest // import "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ctxKeyType string
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
Injections uint64
|
||||||
|
Extractions uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newState(encoded string) state {
|
||||||
|
if encoded == "" {
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
s0, s1, _ := strings.Cut(encoded, ",")
|
||||||
|
injects, _ := strconv.ParseUint(s0, 10, 64)
|
||||||
|
extracts, _ := strconv.ParseUint(s1, 10, 64)
|
||||||
|
return state{
|
||||||
|
Injections: injects,
|
||||||
|
Extractions: extracts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s state) String() string {
|
||||||
|
return fmt.Sprintf("%d,%d", s.Injections, s.Extractions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMapPropagator is a propagation.TextMapPropagator used for testing.
|
||||||
|
type TextMapPropagator struct {
|
||||||
|
name string
|
||||||
|
ctxKey ctxKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapPropagator = (*TextMapPropagator)(nil)
|
||||||
|
|
||||||
|
// NewTextMapPropagator returns a new TextMapPropagator for testing. It will
|
||||||
|
// use name as the key it injects into a TextMapCarrier when Inject is called.
|
||||||
|
func NewTextMapPropagator(name string) *TextMapPropagator {
|
||||||
|
return &TextMapPropagator{name: name, ctxKey: ctxKeyType(name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromContext(ctx context.Context) state {
|
||||||
|
if v := ctx.Value(p.ctxKey); v != nil {
|
||||||
|
if s, ok := v.(state); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromCarrier(carrier propagation.TextMapCarrier) state {
|
||||||
|
return newState(carrier.Get(p.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject sets cross-cutting concerns for p from ctx into carrier.
|
||||||
|
func (p *TextMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
|
||||||
|
s := p.stateFromContext(ctx)
|
||||||
|
s.Injections++
|
||||||
|
carrier.Set(p.name, s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectedN tests if p has made n injections to carrier.
|
||||||
|
func (p *TextMapPropagator) InjectedN(t *testing.T, carrier *TextMapCarrier, n int) bool {
|
||||||
|
if actual := p.stateFromCarrier(carrier).Injections; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} injected %d times, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract reads cross-cutting concerns for p from carrier into ctx.
|
||||||
|
func (p *TextMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
|
||||||
|
s := p.stateFromCarrier(carrier)
|
||||||
|
s.Extractions++
|
||||||
|
return context.WithValue(ctx, p.ctxKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractedN tests if p has made n extractions from the lineage of ctx.
|
||||||
|
// nolint (context is not first arg)
|
||||||
|
func (p *TextMapPropagator) ExtractedN(t *testing.T, ctx context.Context, n int) bool {
|
||||||
|
if actual := p.stateFromContext(ctx).Extractions; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} extracted %d time, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns the name of p as the key who's value is set with Inject.
|
||||||
|
func (p *TextMapPropagator) Fields() []string { return []string{p.name} }
|
@ -0,0 +1,72 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapPropagatorInjectExtract(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
ctx := context.Background()
|
||||||
|
carrier := NewTextMapCarrier(map[string]string{name: value})
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
|
||||||
|
propagator.Inject(ctx, carrier)
|
||||||
|
// Carrier value overridden with state.
|
||||||
|
if carrier.SetKeyValue(t, name, "1,0") {
|
||||||
|
// Ensure nothing has been extracted yet.
|
||||||
|
propagator.ExtractedN(t, ctx, 0)
|
||||||
|
// Test the injection was counted.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = propagator.Extract(ctx, carrier)
|
||||||
|
v := ctx.Value(ctxKeyType(name))
|
||||||
|
if v == nil {
|
||||||
|
t.Error("TextMapPropagator.Extract failed to extract state")
|
||||||
|
}
|
||||||
|
if s, ok := v.(state); !ok {
|
||||||
|
t.Error("TextMapPropagator.Extract did not extract proper state")
|
||||||
|
} else if s.Extractions != 1 {
|
||||||
|
t.Error("TextMapPropagator.Extract did not increment state.Extractions")
|
||||||
|
}
|
||||||
|
if carrier.GotKey(t, name) {
|
||||||
|
// Test the extraction was counted.
|
||||||
|
propagator.ExtractedN(t, ctx, 1)
|
||||||
|
// Ensure no additional injection was recorded.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapPropagatorFields(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
if got := propagator.Fields(); len(got) != 1 {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %d fields, want 1", len(got))
|
||||||
|
} else if got[0] != name {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %q, want %q", got[0], name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewStateEmpty(t *testing.T) {
|
||||||
|
if want, got := (state{}), newState(""); got != want {
|
||||||
|
t.Errorf("newState(\"\") returned %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ import (
|
|||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/exporters/jaeger/internal/internaltest"
|
||||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnvOrWithCollectorEndpointOptionsFromEnv(t *testing.T) {
|
func TestEnvOrWithCollectorEndpointOptionsFromEnv(t *testing.T) {
|
||||||
|
74
exporters/zipkin/internal/internaltest/alignment.go
Normal file
74
exporters/zipkin/internal/internaltest/alignment.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/alignment.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 internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file contains common utilities and objects to validate memory alignment
|
||||||
|
of Go types. The primary use of this functionality is intended to ensure
|
||||||
|
`struct` fields that need to be 64-bit aligned so they can be passed as
|
||||||
|
arguments to 64-bit atomic operations.
|
||||||
|
|
||||||
|
The common workflow is to define a slice of `FieldOffset` and pass them to the
|
||||||
|
`Aligned8Byte` function from within a `TestMain` function from a package's
|
||||||
|
tests. It is important to make this call from the `TestMain` function prior
|
||||||
|
to running the rest of the test suit as it can provide useful diagnostics
|
||||||
|
about field alignment instead of ambiguous nil pointer dereference and runtime
|
||||||
|
panic.
|
||||||
|
|
||||||
|
For more information:
|
||||||
|
https://github.com/open-telemetry/opentelemetry-go/issues/341
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldOffset is a preprocessor representation of a struct field alignment.
|
||||||
|
type FieldOffset struct {
|
||||||
|
// Name of the field.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Offset of the field in bytes.
|
||||||
|
//
|
||||||
|
// To compute this at compile time use unsafe.Offsetof.
|
||||||
|
Offset uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligned8Byte returns if all fields are aligned modulo 8-bytes.
|
||||||
|
//
|
||||||
|
// Error messaging is printed to out for any field determined misaligned.
|
||||||
|
func Aligned8Byte(fields []FieldOffset, out io.Writer) bool {
|
||||||
|
misaligned := make([]FieldOffset, 0)
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Offset%8 != 0 {
|
||||||
|
misaligned = append(misaligned, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(misaligned) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(out, "struct fields not aligned for 64-bit atomic operations:")
|
||||||
|
for _, f := range misaligned {
|
||||||
|
fmt.Fprintf(out, " %s: %d-byte offset\n", f.Name, f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
101
exporters/zipkin/internal/internaltest/env.go
Normal file
101
exporters/zipkin/internal/internaltest/env.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env.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 internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Exists bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvStore stores and recovers environment variables.
|
||||||
|
type EnvStore interface {
|
||||||
|
// Records the environment variable into the store.
|
||||||
|
Record(key string)
|
||||||
|
|
||||||
|
// Restore recovers the environment variables in the store.
|
||||||
|
Restore() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ EnvStore = (*envStore)(nil)
|
||||||
|
|
||||||
|
type envStore struct {
|
||||||
|
store map[string]Env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) add(env Env) {
|
||||||
|
s.store[env.Name] = env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Restore() error {
|
||||||
|
var err error
|
||||||
|
for _, v := range s.store {
|
||||||
|
if v.Exists {
|
||||||
|
err = os.Setenv(v.Name, v.Value)
|
||||||
|
} else {
|
||||||
|
err = os.Unsetenv(v.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) setEnv(key, value string) error {
|
||||||
|
s.Record(key)
|
||||||
|
|
||||||
|
err := os.Setenv(key, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Record(key string) {
|
||||||
|
originValue, exists := os.LookupEnv(key)
|
||||||
|
s.add(Env{
|
||||||
|
Name: key,
|
||||||
|
Value: originValue,
|
||||||
|
Exists: exists,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnvStore() EnvStore {
|
||||||
|
return newEnvStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnvStore() *envStore {
|
||||||
|
return &envStore{store: make(map[string]Env)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetEnvVariables(env map[string]string) (EnvStore, error) {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
for k, v := range env {
|
||||||
|
err := envStore.setEnv(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envStore, nil
|
||||||
|
}
|
237
exporters/zipkin/internal/internaltest/env_test.go
Normal file
237
exporters/zipkin/internal/internaltest/env_test.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnvStoreTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_add() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
e := Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
}
|
||||||
|
envStore.add(e)
|
||||||
|
envStore.add(e)
|
||||||
|
|
||||||
|
s.Assert().Len(envStore.store, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRecord() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "record exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record exists env, but its value is empty",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record not exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Setenv(tc.env.Name, tc.env.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRestore() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvExists: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.add(tc.env)
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.Restore())
|
||||||
|
v, exists := os.LookupEnv(tc.env.Name)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_setEnv() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
key: "name",
|
||||||
|
value: "value",
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "other value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.key)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Setenv(tc.key, "other value"))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.setEnv(tc.key, tc.value))
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
v, exists := os.LookupEnv(tc.key)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvStoreTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(EnvStoreTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetEnvVariables(t *testing.T) {
|
||||||
|
envs := map[string]string{
|
||||||
|
"name1": "value1",
|
||||||
|
"name2": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
for k := range envs {
|
||||||
|
backup.Record(k)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, backup.Restore())
|
||||||
|
}()
|
||||||
|
|
||||||
|
store, err := SetEnvVariables(envs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.IsType(t, &envStore{}, store)
|
||||||
|
concreteStore := store.(*envStore)
|
||||||
|
assert.Len(t, concreteStore.store, 2)
|
||||||
|
assert.Equal(t, backup, concreteStore)
|
||||||
|
}
|
30
exporters/zipkin/internal/internaltest/errors.go
Normal file
30
exporters/zipkin/internal/internaltest/errors.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/errors.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 internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
|
type TestError string
|
||||||
|
|
||||||
|
var _ error = TestError("")
|
||||||
|
|
||||||
|
func NewTestError(s string) error {
|
||||||
|
return TestError(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TestError) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
25
exporters/zipkin/internal/internaltest/gen.go
Normal file
25
exporters/zipkin/internal/internaltest/gen.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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
|
344
exporters/zipkin/internal/internaltest/harness.go
Normal file
344
exporters/zipkin/internal/internaltest/harness.go
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/harness.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 internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/internal/matchers"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Harness is a testing harness used to test implementations of the
|
||||||
|
// OpenTelemetry API.
|
||||||
|
type Harness struct {
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHarness returns an instantiated *Harness using t.
|
||||||
|
func NewHarness(t *testing.T) *Harness {
|
||||||
|
return &Harness{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracerProvider runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// TracerProvider API.
|
||||||
|
func (h *Harness) TestTracerProvider(subjectFactory func() trace.TracerProvider) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("allow creating an arbitrary number of TracerProvider instances", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
e.Expect(tp1).NotToEqual(tp2)
|
||||||
|
})
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
runner := func(tp trace.TracerProvider) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.TracerProvider) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name, version string) {
|
||||||
|
_ = tp.Tracer(name, trace.WithInstrumentationVersion(version))
|
||||||
|
wg.Done()
|
||||||
|
}(fmt.Sprintf("tracer %d", i%5), fmt.Sprintf("%d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
matchers.NewExpecter(t).Expect(func() {
|
||||||
|
// Run with multiple TracerProvider to ensure they encapsulate
|
||||||
|
// their own Tracers.
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
done1 := runner(tp1)
|
||||||
|
done2 := runner(tp2)
|
||||||
|
|
||||||
|
<-done1
|
||||||
|
<-done2
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracer runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// Tracer API.
|
||||||
|
func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("propagates the original context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctxKey := testCtxKey{}
|
||||||
|
ctxValue := "ctx value"
|
||||||
|
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
|
||||||
|
|
||||||
|
ctx, _ = subject.Start(ctx, "test")
|
||||||
|
|
||||||
|
e.Expect(ctx.Value(ctxKey)).ToEqual(ctxValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("returns a span containing the expected properties", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
|
||||||
|
e.Expect(span.SpanContext().IsValid()).ToBeTrue()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stores the span on the provided context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
e.Expect(span.SpanContext()).NotToEqual(trace.SpanContext{})
|
||||||
|
e.Expect(trace.SpanFromContext(ctx)).ToEqual(span)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("starts spans with unique trace and span IDs", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span1 := subject.Start(context.Background(), "span1")
|
||||||
|
_, span2 := subject.Start(context.Background(), "span2")
|
||||||
|
|
||||||
|
sc1 := span1.SpanContext()
|
||||||
|
sc2 := span2.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID())
|
||||||
|
e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates a parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child")
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child")
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
tracer := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := tracer.Start(context.Background(), "span")
|
||||||
|
|
||||||
|
runner := func(tp trace.Tracer) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.Tracer) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name string) {
|
||||||
|
defer wg.Done()
|
||||||
|
_, child := tp.Start(ctx, name)
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
}(fmt.Sprintf("span %d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Expect(func() {
|
||||||
|
done := runner(tracer)
|
||||||
|
|
||||||
|
<-done
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
h.testSpan(subjectFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
|
||||||
|
var methods = map[string]func(span trace.Span){
|
||||||
|
"#End": func(span trace.Span) {
|
||||||
|
span.End()
|
||||||
|
},
|
||||||
|
"#AddEvent": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event")
|
||||||
|
},
|
||||||
|
"#AddEventWithTimestamp": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event", trace.WithTimestamp(time.Now().Add(1*time.Second)))
|
||||||
|
},
|
||||||
|
"#SetStatus": func(span trace.Span) {
|
||||||
|
span.SetStatus(codes.Error, "internal")
|
||||||
|
},
|
||||||
|
"#SetName": func(span trace.Span) {
|
||||||
|
span.SetName("new name")
|
||||||
|
},
|
||||||
|
"#SetAttributes": func(span trace.Span) {
|
||||||
|
span.SetAttributes(attribute.String("key1", "value"), attribute.Int("key2", 123))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var mechanisms = map[string]func() trace.Span{
|
||||||
|
"Span created via Tracer#Start": func() trace.Span {
|
||||||
|
tracer := tracerFactory()
|
||||||
|
_, subject := tracer.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
return subject
|
||||||
|
},
|
||||||
|
"Span created via span.TracerProvider()": func() trace.Span {
|
||||||
|
ctx, spanA := tracerFactory().Start(context.Background(), "span1")
|
||||||
|
|
||||||
|
_, spanB := spanA.TracerProvider().Tracer("second").Start(ctx, "span2")
|
||||||
|
return spanB
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for mechanismName, mechanism := range mechanisms {
|
||||||
|
h.t.Run(mechanismName, func(t *testing.T) {
|
||||||
|
for methodName, method := range methods {
|
||||||
|
t.Run(methodName, func(t *testing.T) {
|
||||||
|
t.Run("is thread-safe", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("#End", func(t *testing.T) {
|
||||||
|
t.Run("can be called multiple times", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
span.End()
|
||||||
|
span.End()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCtxKey struct{}
|
144
exporters/zipkin/internal/internaltest/text_map_carrier.go
Normal file
144
exporters/zipkin/internal/internaltest/text_map_carrier.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier.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 internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextMapCarrier is a storage medium for a TextMapPropagator used in testing.
|
||||||
|
// The methods of a TextMapCarrier are concurrent safe.
|
||||||
|
type TextMapCarrier struct {
|
||||||
|
mtx sync.Mutex
|
||||||
|
|
||||||
|
gets []string
|
||||||
|
sets [][2]string
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapCarrier = (*TextMapCarrier)(nil)
|
||||||
|
|
||||||
|
// NewTextMapCarrier returns a new *TextMapCarrier populated with data.
|
||||||
|
func NewTextMapCarrier(data map[string]string) *TextMapCarrier {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
return &TextMapCarrier{data: copied}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys for which this carrier has a value.
|
||||||
|
func (c *TextMapCarrier) Keys() []string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
result := make([]string, 0, len(c.data))
|
||||||
|
for k := range c.data {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value associated with the passed key.
|
||||||
|
func (c *TextMapCarrier) Get(key string) string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.gets = append(c.gets, key)
|
||||||
|
return c.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotKey tests if c.Get has been called for key.
|
||||||
|
func (c *TextMapCarrier) GotKey(t *testing.T, key string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
for _, k := range c.gets {
|
||||||
|
if k == key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) has not been called", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotN tests if n calls to c.Get have been made.
|
||||||
|
func (c *TextMapCarrier) GotN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.gets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Get was called %d times, not %d", len(c.gets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores the key-value pair.
|
||||||
|
func (c *TextMapCarrier) Set(key, value string) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.sets = append(c.sets, [2]string{key, value})
|
||||||
|
c.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeyValue tests if c.Set has been called for the key-value pair.
|
||||||
|
func (c *TextMapCarrier) SetKeyValue(t *testing.T, key, value string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
var vals []string
|
||||||
|
for _, pair := range c.sets {
|
||||||
|
if key == pair[0] {
|
||||||
|
if value == pair[1] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
vals = append(vals, pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(vals) > 0 {
|
||||||
|
t.Errorf("TextMapCarrier.Set called with %q and %v values, but not %s", key, vals, value)
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) has not been called", key, value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetN tests if n calls to c.Set have been made.
|
||||||
|
func (c *TextMapCarrier) SetN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.sets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Set was called %d times, not %d", len(c.sets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset zeros out the recording state and sets the carried values to data.
|
||||||
|
func (c *TextMapCarrier) Reset(data map[string]string) {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
c.gets = nil
|
||||||
|
c.sets = nil
|
||||||
|
c.data = copied
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, value = "test", "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapCarrierKeys(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
expected, actual := []string{key}, tmc.Keys()
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("expected tmc.Keys() to be %v but it was %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierGet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
if got := tmc.Get("empty"); got != "" {
|
||||||
|
t.Errorf("TextMapCarrier.Get returned %q for an empty key", got)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, "empty")
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
if got := tmc.Get(key); got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) returned %q, want %q", key, got, value)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.GotN(t, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierSet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(nil)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
if got, ok := tmc.data[key]; !ok {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) failed to store pair", key, value)
|
||||||
|
} else if got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) stored (%q,%q), not (%q,%q)", key, value, key, got, key, value)
|
||||||
|
}
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierReset(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear initial data")
|
||||||
|
}
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear data")
|
||||||
|
}
|
||||||
|
}
|
115
exporters/zipkin/internal/internaltest/text_map_propagator.go
Normal file
115
exporters/zipkin/internal/internaltest/text_map_propagator.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator.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 internaltest // import "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ctxKeyType string
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
Injections uint64
|
||||||
|
Extractions uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newState(encoded string) state {
|
||||||
|
if encoded == "" {
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
s0, s1, _ := strings.Cut(encoded, ",")
|
||||||
|
injects, _ := strconv.ParseUint(s0, 10, 64)
|
||||||
|
extracts, _ := strconv.ParseUint(s1, 10, 64)
|
||||||
|
return state{
|
||||||
|
Injections: injects,
|
||||||
|
Extractions: extracts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s state) String() string {
|
||||||
|
return fmt.Sprintf("%d,%d", s.Injections, s.Extractions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMapPropagator is a propagation.TextMapPropagator used for testing.
|
||||||
|
type TextMapPropagator struct {
|
||||||
|
name string
|
||||||
|
ctxKey ctxKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapPropagator = (*TextMapPropagator)(nil)
|
||||||
|
|
||||||
|
// NewTextMapPropagator returns a new TextMapPropagator for testing. It will
|
||||||
|
// use name as the key it injects into a TextMapCarrier when Inject is called.
|
||||||
|
func NewTextMapPropagator(name string) *TextMapPropagator {
|
||||||
|
return &TextMapPropagator{name: name, ctxKey: ctxKeyType(name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromContext(ctx context.Context) state {
|
||||||
|
if v := ctx.Value(p.ctxKey); v != nil {
|
||||||
|
if s, ok := v.(state); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromCarrier(carrier propagation.TextMapCarrier) state {
|
||||||
|
return newState(carrier.Get(p.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject sets cross-cutting concerns for p from ctx into carrier.
|
||||||
|
func (p *TextMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
|
||||||
|
s := p.stateFromContext(ctx)
|
||||||
|
s.Injections++
|
||||||
|
carrier.Set(p.name, s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectedN tests if p has made n injections to carrier.
|
||||||
|
func (p *TextMapPropagator) InjectedN(t *testing.T, carrier *TextMapCarrier, n int) bool {
|
||||||
|
if actual := p.stateFromCarrier(carrier).Injections; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} injected %d times, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract reads cross-cutting concerns for p from carrier into ctx.
|
||||||
|
func (p *TextMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
|
||||||
|
s := p.stateFromCarrier(carrier)
|
||||||
|
s.Extractions++
|
||||||
|
return context.WithValue(ctx, p.ctxKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractedN tests if p has made n extractions from the lineage of ctx.
|
||||||
|
// nolint (context is not first arg)
|
||||||
|
func (p *TextMapPropagator) ExtractedN(t *testing.T, ctx context.Context, n int) bool {
|
||||||
|
if actual := p.stateFromContext(ctx).Extractions; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} extracted %d time, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns the name of p as the key who's value is set with Inject.
|
||||||
|
func (p *TextMapPropagator) Fields() []string { return []string{p.name} }
|
@ -0,0 +1,72 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapPropagatorInjectExtract(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
ctx := context.Background()
|
||||||
|
carrier := NewTextMapCarrier(map[string]string{name: value})
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
|
||||||
|
propagator.Inject(ctx, carrier)
|
||||||
|
// Carrier value overridden with state.
|
||||||
|
if carrier.SetKeyValue(t, name, "1,0") {
|
||||||
|
// Ensure nothing has been extracted yet.
|
||||||
|
propagator.ExtractedN(t, ctx, 0)
|
||||||
|
// Test the injection was counted.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = propagator.Extract(ctx, carrier)
|
||||||
|
v := ctx.Value(ctxKeyType(name))
|
||||||
|
if v == nil {
|
||||||
|
t.Error("TextMapPropagator.Extract failed to extract state")
|
||||||
|
}
|
||||||
|
if s, ok := v.(state); !ok {
|
||||||
|
t.Error("TextMapPropagator.Extract did not extract proper state")
|
||||||
|
} else if s.Extractions != 1 {
|
||||||
|
t.Error("TextMapPropagator.Extract did not increment state.Extractions")
|
||||||
|
}
|
||||||
|
if carrier.GotKey(t, name) {
|
||||||
|
// Test the extraction was counted.
|
||||||
|
propagator.ExtractedN(t, ctx, 1)
|
||||||
|
// Ensure no additional injection was recorded.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapPropagatorFields(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
if got := propagator.Fields(); len(got) != 1 {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %d fields, want 1", len(got))
|
||||||
|
} else if got[0] != name {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %q, want %q", got[0], name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewStateEmpty(t *testing.T) {
|
||||||
|
if want, got := (state{}), newState(""); got != want {
|
||||||
|
t.Errorf("newState(\"\") returned %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/exporters/zipkin/internal/internaltest"
|
||||||
|
|
||||||
"github.com/go-logr/logr/funcr"
|
"github.com/go-logr/logr/funcr"
|
||||||
zkmodel "github.com/openzipkin/zipkin-go/model"
|
zkmodel "github.com/openzipkin/zipkin-go/model"
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/alignment.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env_test.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/errors.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
25
internal/internaltest/gen.go
Normal file
25
internal/internaltest/gen.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/harness.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier_test.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator_test.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
74
internal/shared/internaltest/alignment.go.tmpl
Normal file
74
internal/shared/internaltest/alignment.go.tmpl
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/alignment.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 internaltest
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file contains common utilities and objects to validate memory alignment
|
||||||
|
of Go types. The primary use of this functionality is intended to ensure
|
||||||
|
`struct` fields that need to be 64-bit aligned so they can be passed as
|
||||||
|
arguments to 64-bit atomic operations.
|
||||||
|
|
||||||
|
The common workflow is to define a slice of `FieldOffset` and pass them to the
|
||||||
|
`Aligned8Byte` function from within a `TestMain` function from a package's
|
||||||
|
tests. It is important to make this call from the `TestMain` function prior
|
||||||
|
to running the rest of the test suit as it can provide useful diagnostics
|
||||||
|
about field alignment instead of ambiguous nil pointer dereference and runtime
|
||||||
|
panic.
|
||||||
|
|
||||||
|
For more information:
|
||||||
|
https://github.com/open-telemetry/opentelemetry-go/issues/341
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldOffset is a preprocessor representation of a struct field alignment.
|
||||||
|
type FieldOffset struct {
|
||||||
|
// Name of the field.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Offset of the field in bytes.
|
||||||
|
//
|
||||||
|
// To compute this at compile time use unsafe.Offsetof.
|
||||||
|
Offset uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligned8Byte returns if all fields are aligned modulo 8-bytes.
|
||||||
|
//
|
||||||
|
// Error messaging is printed to out for any field determined misaligned.
|
||||||
|
func Aligned8Byte(fields []FieldOffset, out io.Writer) bool {
|
||||||
|
misaligned := make([]FieldOffset, 0)
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Offset%8 != 0 {
|
||||||
|
misaligned = append(misaligned, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(misaligned) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(out, "struct fields not aligned for 64-bit atomic operations:")
|
||||||
|
for _, f := range misaligned {
|
||||||
|
fmt.Fprintf(out, " %s: %d-byte offset\n", f.Name, f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
101
internal/shared/internaltest/env.go.tmpl
Normal file
101
internal/shared/internaltest/env.go.tmpl
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Exists bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvStore stores and recovers environment variables.
|
||||||
|
type EnvStore interface {
|
||||||
|
// Records the environment variable into the store.
|
||||||
|
Record(key string)
|
||||||
|
|
||||||
|
// Restore recovers the environment variables in the store.
|
||||||
|
Restore() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ EnvStore = (*envStore)(nil)
|
||||||
|
|
||||||
|
type envStore struct {
|
||||||
|
store map[string]Env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) add(env Env) {
|
||||||
|
s.store[env.Name] = env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Restore() error {
|
||||||
|
var err error
|
||||||
|
for _, v := range s.store {
|
||||||
|
if v.Exists {
|
||||||
|
err = os.Setenv(v.Name, v.Value)
|
||||||
|
} else {
|
||||||
|
err = os.Unsetenv(v.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) setEnv(key, value string) error {
|
||||||
|
s.Record(key)
|
||||||
|
|
||||||
|
err := os.Setenv(key, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Record(key string) {
|
||||||
|
originValue, exists := os.LookupEnv(key)
|
||||||
|
s.add(Env{
|
||||||
|
Name: key,
|
||||||
|
Value: originValue,
|
||||||
|
Exists: exists,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnvStore() EnvStore {
|
||||||
|
return newEnvStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnvStore() *envStore {
|
||||||
|
return &envStore{store: make(map[string]Env)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetEnvVariables(env map[string]string) (EnvStore, error) {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
for k, v := range env {
|
||||||
|
err := envStore.setEnv(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envStore, nil
|
||||||
|
}
|
237
internal/shared/internaltest/env_test.go.tmpl
Normal file
237
internal/shared/internaltest/env_test.go.tmpl
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnvStoreTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_add() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
e := Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
}
|
||||||
|
envStore.add(e)
|
||||||
|
envStore.add(e)
|
||||||
|
|
||||||
|
s.Assert().Len(envStore.store, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRecord() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "record exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record exists env, but its value is empty",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record not exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Setenv(tc.env.Name, tc.env.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRestore() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvExists: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.add(tc.env)
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.Restore())
|
||||||
|
v, exists := os.LookupEnv(tc.env.Name)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_setEnv() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
key: "name",
|
||||||
|
value: "value",
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "other value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.key)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Setenv(tc.key, "other value"))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.setEnv(tc.key, tc.value))
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
v, exists := os.LookupEnv(tc.key)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvStoreTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(EnvStoreTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetEnvVariables(t *testing.T) {
|
||||||
|
envs := map[string]string{
|
||||||
|
"name1": "value1",
|
||||||
|
"name2": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
for k := range envs {
|
||||||
|
backup.Record(k)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, backup.Restore())
|
||||||
|
}()
|
||||||
|
|
||||||
|
store, err := SetEnvVariables(envs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.IsType(t, &envStore{}, store)
|
||||||
|
concreteStore := store.(*envStore)
|
||||||
|
assert.Len(t, concreteStore.store, 2)
|
||||||
|
assert.Equal(t, backup, concreteStore)
|
||||||
|
}
|
30
internal/shared/internaltest/errors.go.tmpl
Normal file
30
internal/shared/internaltest/errors.go.tmpl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/errors.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 internaltest
|
||||||
|
|
||||||
|
type TestError string
|
||||||
|
|
||||||
|
var _ error = TestError("")
|
||||||
|
|
||||||
|
func NewTestError(s string) error {
|
||||||
|
return TestError(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TestError) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
344
internal/shared/internaltest/harness.go.tmpl
Normal file
344
internal/shared/internaltest/harness.go.tmpl
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/harness.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/internal/matchers"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Harness is a testing harness used to test implementations of the
|
||||||
|
// OpenTelemetry API.
|
||||||
|
type Harness struct {
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHarness returns an instantiated *Harness using t.
|
||||||
|
func NewHarness(t *testing.T) *Harness {
|
||||||
|
return &Harness{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracerProvider runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// TracerProvider API.
|
||||||
|
func (h *Harness) TestTracerProvider(subjectFactory func() trace.TracerProvider) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("allow creating an arbitrary number of TracerProvider instances", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
e.Expect(tp1).NotToEqual(tp2)
|
||||||
|
})
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
runner := func(tp trace.TracerProvider) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.TracerProvider) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name, version string) {
|
||||||
|
_ = tp.Tracer(name, trace.WithInstrumentationVersion(version))
|
||||||
|
wg.Done()
|
||||||
|
}(fmt.Sprintf("tracer %d", i%5), fmt.Sprintf("%d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
matchers.NewExpecter(t).Expect(func() {
|
||||||
|
// Run with multiple TracerProvider to ensure they encapsulate
|
||||||
|
// their own Tracers.
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
done1 := runner(tp1)
|
||||||
|
done2 := runner(tp2)
|
||||||
|
|
||||||
|
<-done1
|
||||||
|
<-done2
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracer runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// Tracer API.
|
||||||
|
func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("propagates the original context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctxKey := testCtxKey{}
|
||||||
|
ctxValue := "ctx value"
|
||||||
|
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
|
||||||
|
|
||||||
|
ctx, _ = subject.Start(ctx, "test")
|
||||||
|
|
||||||
|
e.Expect(ctx.Value(ctxKey)).ToEqual(ctxValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("returns a span containing the expected properties", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
|
||||||
|
e.Expect(span.SpanContext().IsValid()).ToBeTrue()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stores the span on the provided context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
e.Expect(span.SpanContext()).NotToEqual(trace.SpanContext{})
|
||||||
|
e.Expect(trace.SpanFromContext(ctx)).ToEqual(span)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("starts spans with unique trace and span IDs", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span1 := subject.Start(context.Background(), "span1")
|
||||||
|
_, span2 := subject.Start(context.Background(), "span2")
|
||||||
|
|
||||||
|
sc1 := span1.SpanContext()
|
||||||
|
sc2 := span2.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID())
|
||||||
|
e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates a parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child")
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child")
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
tracer := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := tracer.Start(context.Background(), "span")
|
||||||
|
|
||||||
|
runner := func(tp trace.Tracer) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.Tracer) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name string) {
|
||||||
|
defer wg.Done()
|
||||||
|
_, child := tp.Start(ctx, name)
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
}(fmt.Sprintf("span %d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Expect(func() {
|
||||||
|
done := runner(tracer)
|
||||||
|
|
||||||
|
<-done
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
h.testSpan(subjectFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
|
||||||
|
var methods = map[string]func(span trace.Span){
|
||||||
|
"#End": func(span trace.Span) {
|
||||||
|
span.End()
|
||||||
|
},
|
||||||
|
"#AddEvent": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event")
|
||||||
|
},
|
||||||
|
"#AddEventWithTimestamp": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event", trace.WithTimestamp(time.Now().Add(1*time.Second)))
|
||||||
|
},
|
||||||
|
"#SetStatus": func(span trace.Span) {
|
||||||
|
span.SetStatus(codes.Error, "internal")
|
||||||
|
},
|
||||||
|
"#SetName": func(span trace.Span) {
|
||||||
|
span.SetName("new name")
|
||||||
|
},
|
||||||
|
"#SetAttributes": func(span trace.Span) {
|
||||||
|
span.SetAttributes(attribute.String("key1", "value"), attribute.Int("key2", 123))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var mechanisms = map[string]func() trace.Span{
|
||||||
|
"Span created via Tracer#Start": func() trace.Span {
|
||||||
|
tracer := tracerFactory()
|
||||||
|
_, subject := tracer.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
return subject
|
||||||
|
},
|
||||||
|
"Span created via span.TracerProvider()": func() trace.Span {
|
||||||
|
ctx, spanA := tracerFactory().Start(context.Background(), "span1")
|
||||||
|
|
||||||
|
_, spanB := spanA.TracerProvider().Tracer("second").Start(ctx, "span2")
|
||||||
|
return spanB
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for mechanismName, mechanism := range mechanisms {
|
||||||
|
h.t.Run(mechanismName, func(t *testing.T) {
|
||||||
|
for methodName, method := range methods {
|
||||||
|
t.Run(methodName, func(t *testing.T) {
|
||||||
|
t.Run("is thread-safe", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("#End", func(t *testing.T) {
|
||||||
|
t.Run("can be called multiple times", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
span.End()
|
||||||
|
span.End()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCtxKey struct{}
|
144
internal/shared/internaltest/text_map_carrier.go.tmpl
Normal file
144
internal/shared/internaltest/text_map_carrier.go.tmpl
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextMapCarrier is a storage medium for a TextMapPropagator used in testing.
|
||||||
|
// The methods of a TextMapCarrier are concurrent safe.
|
||||||
|
type TextMapCarrier struct {
|
||||||
|
mtx sync.Mutex
|
||||||
|
|
||||||
|
gets []string
|
||||||
|
sets [][2]string
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapCarrier = (*TextMapCarrier)(nil)
|
||||||
|
|
||||||
|
// NewTextMapCarrier returns a new *TextMapCarrier populated with data.
|
||||||
|
func NewTextMapCarrier(data map[string]string) *TextMapCarrier {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
return &TextMapCarrier{data: copied}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys for which this carrier has a value.
|
||||||
|
func (c *TextMapCarrier) Keys() []string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
result := make([]string, 0, len(c.data))
|
||||||
|
for k := range c.data {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value associated with the passed key.
|
||||||
|
func (c *TextMapCarrier) Get(key string) string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.gets = append(c.gets, key)
|
||||||
|
return c.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotKey tests if c.Get has been called for key.
|
||||||
|
func (c *TextMapCarrier) GotKey(t *testing.T, key string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
for _, k := range c.gets {
|
||||||
|
if k == key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) has not been called", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotN tests if n calls to c.Get have been made.
|
||||||
|
func (c *TextMapCarrier) GotN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.gets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Get was called %d times, not %d", len(c.gets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores the key-value pair.
|
||||||
|
func (c *TextMapCarrier) Set(key, value string) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.sets = append(c.sets, [2]string{key, value})
|
||||||
|
c.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeyValue tests if c.Set has been called for the key-value pair.
|
||||||
|
func (c *TextMapCarrier) SetKeyValue(t *testing.T, key, value string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
var vals []string
|
||||||
|
for _, pair := range c.sets {
|
||||||
|
if key == pair[0] {
|
||||||
|
if value == pair[1] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
vals = append(vals, pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(vals) > 0 {
|
||||||
|
t.Errorf("TextMapCarrier.Set called with %q and %v values, but not %s", key, vals, value)
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) has not been called", key, value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetN tests if n calls to c.Set have been made.
|
||||||
|
func (c *TextMapCarrier) SetN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.sets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Set was called %d times, not %d", len(c.sets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset zeros out the recording state and sets the carried values to data.
|
||||||
|
func (c *TextMapCarrier) Reset(data map[string]string) {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
c.gets = nil
|
||||||
|
c.sets = nil
|
||||||
|
c.data = copied
|
||||||
|
}
|
86
internal/shared/internaltest/text_map_carrier_test.go.tmpl
Normal file
86
internal/shared/internaltest/text_map_carrier_test.go.tmpl
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, value = "test", "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapCarrierKeys(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
expected, actual := []string{key}, tmc.Keys()
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("expected tmc.Keys() to be %v but it was %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierGet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
if got := tmc.Get("empty"); got != "" {
|
||||||
|
t.Errorf("TextMapCarrier.Get returned %q for an empty key", got)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, "empty")
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
if got := tmc.Get(key); got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) returned %q, want %q", key, got, value)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.GotN(t, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierSet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(nil)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
if got, ok := tmc.data[key]; !ok {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) failed to store pair", key, value)
|
||||||
|
} else if got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) stored (%q,%q), not (%q,%q)", key, value, key, got, key, value)
|
||||||
|
}
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierReset(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear initial data")
|
||||||
|
}
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear data")
|
||||||
|
}
|
||||||
|
}
|
115
internal/shared/internaltest/text_map_propagator.go.tmpl
Normal file
115
internal/shared/internaltest/text_map_propagator.go.tmpl
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ctxKeyType string
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
Injections uint64
|
||||||
|
Extractions uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newState(encoded string) state {
|
||||||
|
if encoded == "" {
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
s0, s1, _ := strings.Cut(encoded, ",")
|
||||||
|
injects, _ := strconv.ParseUint(s0, 10, 64)
|
||||||
|
extracts, _ := strconv.ParseUint(s1, 10, 64)
|
||||||
|
return state{
|
||||||
|
Injections: injects,
|
||||||
|
Extractions: extracts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s state) String() string {
|
||||||
|
return fmt.Sprintf("%d,%d", s.Injections, s.Extractions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMapPropagator is a propagation.TextMapPropagator used for testing.
|
||||||
|
type TextMapPropagator struct {
|
||||||
|
name string
|
||||||
|
ctxKey ctxKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapPropagator = (*TextMapPropagator)(nil)
|
||||||
|
|
||||||
|
// NewTextMapPropagator returns a new TextMapPropagator for testing. It will
|
||||||
|
// use name as the key it injects into a TextMapCarrier when Inject is called.
|
||||||
|
func NewTextMapPropagator(name string) *TextMapPropagator {
|
||||||
|
return &TextMapPropagator{name: name, ctxKey: ctxKeyType(name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromContext(ctx context.Context) state {
|
||||||
|
if v := ctx.Value(p.ctxKey); v != nil {
|
||||||
|
if s, ok := v.(state); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromCarrier(carrier propagation.TextMapCarrier) state {
|
||||||
|
return newState(carrier.Get(p.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject sets cross-cutting concerns for p from ctx into carrier.
|
||||||
|
func (p *TextMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
|
||||||
|
s := p.stateFromContext(ctx)
|
||||||
|
s.Injections++
|
||||||
|
carrier.Set(p.name, s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectedN tests if p has made n injections to carrier.
|
||||||
|
func (p *TextMapPropagator) InjectedN(t *testing.T, carrier *TextMapCarrier, n int) bool {
|
||||||
|
if actual := p.stateFromCarrier(carrier).Injections; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} injected %d times, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract reads cross-cutting concerns for p from carrier into ctx.
|
||||||
|
func (p *TextMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
|
||||||
|
s := p.stateFromCarrier(carrier)
|
||||||
|
s.Extractions++
|
||||||
|
return context.WithValue(ctx, p.ctxKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractedN tests if p has made n extractions from the lineage of ctx.
|
||||||
|
// nolint (context is not first arg)
|
||||||
|
func (p *TextMapPropagator) ExtractedN(t *testing.T, ctx context.Context, n int) bool {
|
||||||
|
if actual := p.stateFromContext(ctx).Extractions; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} extracted %d time, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns the name of p as the key who's value is set with Inject.
|
||||||
|
func (p *TextMapPropagator) Fields() []string { return []string{p.name} }
|
@ -0,0 +1,72 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapPropagatorInjectExtract(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
ctx := context.Background()
|
||||||
|
carrier := NewTextMapCarrier(map[string]string{name: value})
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
|
||||||
|
propagator.Inject(ctx, carrier)
|
||||||
|
// Carrier value overridden with state.
|
||||||
|
if carrier.SetKeyValue(t, name, "1,0") {
|
||||||
|
// Ensure nothing has been extracted yet.
|
||||||
|
propagator.ExtractedN(t, ctx, 0)
|
||||||
|
// Test the injection was counted.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = propagator.Extract(ctx, carrier)
|
||||||
|
v := ctx.Value(ctxKeyType(name))
|
||||||
|
if v == nil {
|
||||||
|
t.Error("TextMapPropagator.Extract failed to extract state")
|
||||||
|
}
|
||||||
|
if s, ok := v.(state); !ok {
|
||||||
|
t.Error("TextMapPropagator.Extract did not extract proper state")
|
||||||
|
} else if s.Extractions != 1 {
|
||||||
|
t.Error("TextMapPropagator.Extract did not increment state.Extractions")
|
||||||
|
}
|
||||||
|
if carrier.GotKey(t, name) {
|
||||||
|
// Test the extraction was counted.
|
||||||
|
propagator.ExtractedN(t, ctx, 1)
|
||||||
|
// Ensure no additional injection was recorded.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapPropagatorFields(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
if got := propagator.Fields(); len(got) != 1 {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %d fields, want 1", len(got))
|
||||||
|
} else if got[0] != name {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %q, want %q", got[0], name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewStateEmpty(t *testing.T) {
|
||||||
|
if want, got := (state{}), newState(""); got != want {
|
||||||
|
t.Errorf("newState(\"\") returned %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
2
sdk/internal/env/env_test.go
vendored
2
sdk/internal/env/env_test.go
vendored
@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnvParse(t *testing.T) {
|
func TestEnvParse(t *testing.T) {
|
||||||
|
74
sdk/internal/internaltest/alignment.go
Normal file
74
sdk/internal/internaltest/alignment.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/alignment.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 internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file contains common utilities and objects to validate memory alignment
|
||||||
|
of Go types. The primary use of this functionality is intended to ensure
|
||||||
|
`struct` fields that need to be 64-bit aligned so they can be passed as
|
||||||
|
arguments to 64-bit atomic operations.
|
||||||
|
|
||||||
|
The common workflow is to define a slice of `FieldOffset` and pass them to the
|
||||||
|
`Aligned8Byte` function from within a `TestMain` function from a package's
|
||||||
|
tests. It is important to make this call from the `TestMain` function prior
|
||||||
|
to running the rest of the test suit as it can provide useful diagnostics
|
||||||
|
about field alignment instead of ambiguous nil pointer dereference and runtime
|
||||||
|
panic.
|
||||||
|
|
||||||
|
For more information:
|
||||||
|
https://github.com/open-telemetry/opentelemetry-go/issues/341
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldOffset is a preprocessor representation of a struct field alignment.
|
||||||
|
type FieldOffset struct {
|
||||||
|
// Name of the field.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Offset of the field in bytes.
|
||||||
|
//
|
||||||
|
// To compute this at compile time use unsafe.Offsetof.
|
||||||
|
Offset uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligned8Byte returns if all fields are aligned modulo 8-bytes.
|
||||||
|
//
|
||||||
|
// Error messaging is printed to out for any field determined misaligned.
|
||||||
|
func Aligned8Byte(fields []FieldOffset, out io.Writer) bool {
|
||||||
|
misaligned := make([]FieldOffset, 0)
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Offset%8 != 0 {
|
||||||
|
misaligned = append(misaligned, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(misaligned) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(out, "struct fields not aligned for 64-bit atomic operations:")
|
||||||
|
for _, f := range misaligned {
|
||||||
|
fmt.Fprintf(out, " %s: %d-byte offset\n", f.Name, f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
101
sdk/internal/internaltest/env.go
Normal file
101
sdk/internal/internaltest/env.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env.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 internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Exists bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvStore stores and recovers environment variables.
|
||||||
|
type EnvStore interface {
|
||||||
|
// Records the environment variable into the store.
|
||||||
|
Record(key string)
|
||||||
|
|
||||||
|
// Restore recovers the environment variables in the store.
|
||||||
|
Restore() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ EnvStore = (*envStore)(nil)
|
||||||
|
|
||||||
|
type envStore struct {
|
||||||
|
store map[string]Env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) add(env Env) {
|
||||||
|
s.store[env.Name] = env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Restore() error {
|
||||||
|
var err error
|
||||||
|
for _, v := range s.store {
|
||||||
|
if v.Exists {
|
||||||
|
err = os.Setenv(v.Name, v.Value)
|
||||||
|
} else {
|
||||||
|
err = os.Unsetenv(v.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) setEnv(key, value string) error {
|
||||||
|
s.Record(key)
|
||||||
|
|
||||||
|
err := os.Setenv(key, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *envStore) Record(key string) {
|
||||||
|
originValue, exists := os.LookupEnv(key)
|
||||||
|
s.add(Env{
|
||||||
|
Name: key,
|
||||||
|
Value: originValue,
|
||||||
|
Exists: exists,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnvStore() EnvStore {
|
||||||
|
return newEnvStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnvStore() *envStore {
|
||||||
|
return &envStore{store: make(map[string]Env)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetEnvVariables(env map[string]string) (EnvStore, error) {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
for k, v := range env {
|
||||||
|
err := envStore.setEnv(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envStore, nil
|
||||||
|
}
|
237
sdk/internal/internaltest/env_test.go
Normal file
237
sdk/internal/internaltest/env_test.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/env_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnvStoreTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_add() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
e := Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
}
|
||||||
|
envStore.add(e)
|
||||||
|
envStore.add(e)
|
||||||
|
|
||||||
|
s.Assert().Len(envStore.store, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRecord() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "record exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record exists env, but its value is empty",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "record not exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Setenv(tc.env.Name, tc.env.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
|
||||||
|
if tc.env.Exists {
|
||||||
|
s.Assert().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) TestRestore() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env Env
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Value: "value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no exists env",
|
||||||
|
env: Env{
|
||||||
|
Name: "name",
|
||||||
|
Exists: false,
|
||||||
|
},
|
||||||
|
expectedEnvExists: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
envStore.add(tc.env)
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.env.Name)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Unsetenv(tc.env.Name))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.Restore())
|
||||||
|
v, exists := os.LookupEnv(tc.env.Name)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvStoreTestSuite) Test_setEnv() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
expectedEnvStore *envStore
|
||||||
|
expectedEnvValue string
|
||||||
|
expectedEnvExists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
key: "name",
|
||||||
|
value: "value",
|
||||||
|
expectedEnvStore: &envStore{store: map[string]Env{
|
||||||
|
"name": {
|
||||||
|
Name: "name",
|
||||||
|
Value: "other value",
|
||||||
|
Exists: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedEnvValue: "value",
|
||||||
|
expectedEnvExists: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
envStore := newEnvStore()
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
backup.Record(tc.key)
|
||||||
|
|
||||||
|
s.Require().NoError(os.Setenv(tc.key, "other value"))
|
||||||
|
|
||||||
|
s.Assert().NoError(envStore.setEnv(tc.key, tc.value))
|
||||||
|
s.Assert().Equal(tc.expectedEnvStore, envStore)
|
||||||
|
v, exists := os.LookupEnv(tc.key)
|
||||||
|
s.Assert().Equal(tc.expectedEnvValue, v)
|
||||||
|
s.Assert().Equal(tc.expectedEnvExists, exists)
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
s.Require().NoError(backup.Restore())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvStoreTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(EnvStoreTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetEnvVariables(t *testing.T) {
|
||||||
|
envs := map[string]string{
|
||||||
|
"name1": "value1",
|
||||||
|
"name2": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backup := newEnvStore()
|
||||||
|
for k := range envs {
|
||||||
|
backup.Record(k)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, backup.Restore())
|
||||||
|
}()
|
||||||
|
|
||||||
|
store, err := SetEnvVariables(envs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.IsType(t, &envStore{}, store)
|
||||||
|
concreteStore := store.(*envStore)
|
||||||
|
assert.Len(t, concreteStore.store, 2)
|
||||||
|
assert.Equal(t, backup, concreteStore)
|
||||||
|
}
|
30
sdk/internal/internaltest/errors.go
Normal file
30
sdk/internal/internaltest/errors.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/errors.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 internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
|
type TestError string
|
||||||
|
|
||||||
|
var _ error = TestError("")
|
||||||
|
|
||||||
|
func NewTestError(s string) error {
|
||||||
|
return TestError(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TestError) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
25
sdk/internal/internaltest/gen.go
Normal file
25
sdk/internal/internaltest/gen.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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
|
344
sdk/internal/internaltest/harness.go
Normal file
344
sdk/internal/internaltest/harness.go
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/harness.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 internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/internal/matchers"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Harness is a testing harness used to test implementations of the
|
||||||
|
// OpenTelemetry API.
|
||||||
|
type Harness struct {
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHarness returns an instantiated *Harness using t.
|
||||||
|
func NewHarness(t *testing.T) *Harness {
|
||||||
|
return &Harness{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracerProvider runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// TracerProvider API.
|
||||||
|
func (h *Harness) TestTracerProvider(subjectFactory func() trace.TracerProvider) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("allow creating an arbitrary number of TracerProvider instances", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
e.Expect(tp1).NotToEqual(tp2)
|
||||||
|
})
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
runner := func(tp trace.TracerProvider) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.TracerProvider) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name, version string) {
|
||||||
|
_ = tp.Tracer(name, trace.WithInstrumentationVersion(version))
|
||||||
|
wg.Done()
|
||||||
|
}(fmt.Sprintf("tracer %d", i%5), fmt.Sprintf("%d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
matchers.NewExpecter(t).Expect(func() {
|
||||||
|
// Run with multiple TracerProvider to ensure they encapsulate
|
||||||
|
// their own Tracers.
|
||||||
|
tp1 := subjectFactory()
|
||||||
|
tp2 := subjectFactory()
|
||||||
|
|
||||||
|
done1 := runner(tp1)
|
||||||
|
done2 := runner(tp2)
|
||||||
|
|
||||||
|
<-done1
|
||||||
|
<-done2
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTracer runs validation tests for an implementation of the OpenTelemetry
|
||||||
|
// Tracer API.
|
||||||
|
func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
|
||||||
|
h.t.Run("#Start", func(t *testing.T) {
|
||||||
|
t.Run("propagates the original context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctxKey := testCtxKey{}
|
||||||
|
ctxValue := "ctx value"
|
||||||
|
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
|
||||||
|
|
||||||
|
ctx, _ = subject.Start(ctx, "test")
|
||||||
|
|
||||||
|
e.Expect(ctx.Value(ctxKey)).ToEqual(ctxValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("returns a span containing the expected properties", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
|
||||||
|
e.Expect(span.SpanContext().IsValid()).ToBeTrue()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stores the span on the provided context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, span := subject.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
e.Expect(span).NotToBeNil()
|
||||||
|
e.Expect(span.SpanContext()).NotToEqual(trace.SpanContext{})
|
||||||
|
e.Expect(trace.SpanFromContext(ctx)).ToEqual(span)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("starts spans with unique trace and span IDs", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, span1 := subject.Start(context.Background(), "span1")
|
||||||
|
_, span2 := subject.Start(context.Background(), "span2")
|
||||||
|
|
||||||
|
sc1 := span1.SpanContext()
|
||||||
|
sc2 := span2.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID())
|
||||||
|
e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates a parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child")
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
|
_, child := subject.Start(ctx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child")
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
tracer := subjectFactory()
|
||||||
|
|
||||||
|
ctx, parent := tracer.Start(context.Background(), "span")
|
||||||
|
|
||||||
|
runner := func(tp trace.Tracer) <-chan struct{} {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func(tp trace.Tracer) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(name string) {
|
||||||
|
defer wg.Done()
|
||||||
|
_, child := tp.Start(ctx, name)
|
||||||
|
|
||||||
|
psc := parent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
|
||||||
|
e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
|
||||||
|
}(fmt.Sprintf("span %d", i))
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}(tp)
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Expect(func() {
|
||||||
|
done := runner(tracer)
|
||||||
|
|
||||||
|
<-done
|
||||||
|
}).NotToPanic()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
h.testSpan(subjectFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
|
||||||
|
var methods = map[string]func(span trace.Span){
|
||||||
|
"#End": func(span trace.Span) {
|
||||||
|
span.End()
|
||||||
|
},
|
||||||
|
"#AddEvent": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event")
|
||||||
|
},
|
||||||
|
"#AddEventWithTimestamp": func(span trace.Span) {
|
||||||
|
span.AddEvent("test event", trace.WithTimestamp(time.Now().Add(1*time.Second)))
|
||||||
|
},
|
||||||
|
"#SetStatus": func(span trace.Span) {
|
||||||
|
span.SetStatus(codes.Error, "internal")
|
||||||
|
},
|
||||||
|
"#SetName": func(span trace.Span) {
|
||||||
|
span.SetName("new name")
|
||||||
|
},
|
||||||
|
"#SetAttributes": func(span trace.Span) {
|
||||||
|
span.SetAttributes(attribute.String("key1", "value"), attribute.Int("key2", 123))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var mechanisms = map[string]func() trace.Span{
|
||||||
|
"Span created via Tracer#Start": func() trace.Span {
|
||||||
|
tracer := tracerFactory()
|
||||||
|
_, subject := tracer.Start(context.Background(), "test")
|
||||||
|
|
||||||
|
return subject
|
||||||
|
},
|
||||||
|
"Span created via span.TracerProvider()": func() trace.Span {
|
||||||
|
ctx, spanA := tracerFactory().Start(context.Background(), "span1")
|
||||||
|
|
||||||
|
_, spanB := spanA.TracerProvider().Tracer("second").Start(ctx, "span2")
|
||||||
|
return spanB
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for mechanismName, mechanism := range mechanisms {
|
||||||
|
h.t.Run(mechanismName, func(t *testing.T) {
|
||||||
|
for methodName, method := range methods {
|
||||||
|
t.Run(methodName, func(t *testing.T) {
|
||||||
|
t.Run("is thread-safe", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
method(span)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("#End", func(t *testing.T) {
|
||||||
|
t.Run("can be called multiple times", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
span := mechanism()
|
||||||
|
|
||||||
|
span.End()
|
||||||
|
span.End()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCtxKey struct{}
|
144
sdk/internal/internaltest/text_map_carrier.go
Normal file
144
sdk/internal/internaltest/text_map_carrier.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier.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 internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextMapCarrier is a storage medium for a TextMapPropagator used in testing.
|
||||||
|
// The methods of a TextMapCarrier are concurrent safe.
|
||||||
|
type TextMapCarrier struct {
|
||||||
|
mtx sync.Mutex
|
||||||
|
|
||||||
|
gets []string
|
||||||
|
sets [][2]string
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapCarrier = (*TextMapCarrier)(nil)
|
||||||
|
|
||||||
|
// NewTextMapCarrier returns a new *TextMapCarrier populated with data.
|
||||||
|
func NewTextMapCarrier(data map[string]string) *TextMapCarrier {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
return &TextMapCarrier{data: copied}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys for which this carrier has a value.
|
||||||
|
func (c *TextMapCarrier) Keys() []string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
result := make([]string, 0, len(c.data))
|
||||||
|
for k := range c.data {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value associated with the passed key.
|
||||||
|
func (c *TextMapCarrier) Get(key string) string {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.gets = append(c.gets, key)
|
||||||
|
return c.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotKey tests if c.Get has been called for key.
|
||||||
|
func (c *TextMapCarrier) GotKey(t *testing.T, key string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
for _, k := range c.gets {
|
||||||
|
if k == key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) has not been called", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GotN tests if n calls to c.Get have been made.
|
||||||
|
func (c *TextMapCarrier) GotN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.gets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Get was called %d times, not %d", len(c.gets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores the key-value pair.
|
||||||
|
func (c *TextMapCarrier) Set(key, value string) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.sets = append(c.sets, [2]string{key, value})
|
||||||
|
c.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeyValue tests if c.Set has been called for the key-value pair.
|
||||||
|
func (c *TextMapCarrier) SetKeyValue(t *testing.T, key, value string) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
var vals []string
|
||||||
|
for _, pair := range c.sets {
|
||||||
|
if key == pair[0] {
|
||||||
|
if value == pair[1] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
vals = append(vals, pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(vals) > 0 {
|
||||||
|
t.Errorf("TextMapCarrier.Set called with %q and %v values, but not %s", key, vals, value)
|
||||||
|
}
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) has not been called", key, value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetN tests if n calls to c.Set have been made.
|
||||||
|
func (c *TextMapCarrier) SetN(t *testing.T, n int) bool {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if len(c.sets) != n {
|
||||||
|
t.Errorf("TextMapCarrier.Set was called %d times, not %d", len(c.sets), n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset zeros out the recording state and sets the carried values to data.
|
||||||
|
func (c *TextMapCarrier) Reset(data map[string]string) {
|
||||||
|
copied := make(map[string]string, len(data))
|
||||||
|
for k, v := range data {
|
||||||
|
copied[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
|
c.gets = nil
|
||||||
|
c.sets = nil
|
||||||
|
c.data = copied
|
||||||
|
}
|
86
sdk/internal/internaltest/text_map_carrier_test.go
Normal file
86
sdk/internal/internaltest/text_map_carrier_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_carrier_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, value = "test", "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapCarrierKeys(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
expected, actual := []string{key}, tmc.Keys()
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("expected tmc.Keys() to be %v but it was %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierGet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
if got := tmc.Get("empty"); got != "" {
|
||||||
|
t.Errorf("TextMapCarrier.Get returned %q for an empty key", got)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, "empty")
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
if got := tmc.Get(key); got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Get(%q) returned %q, want %q", key, got, value)
|
||||||
|
}
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.GotN(t, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierSet(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(nil)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
if got, ok := tmc.data[key]; !ok {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) failed to store pair", key, value)
|
||||||
|
} else if got != value {
|
||||||
|
t.Errorf("TextMapCarrier.Set(%q,%q) stored (%q,%q), not (%q,%q)", key, value, key, got, key, value)
|
||||||
|
}
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapCarrierReset(t *testing.T) {
|
||||||
|
tmc := NewTextMapCarrier(map[string]string{key: value})
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear initial data")
|
||||||
|
}
|
||||||
|
tmc.GotN(t, 1)
|
||||||
|
tmc.GotKey(t, key)
|
||||||
|
tmc.Set(key, value)
|
||||||
|
tmc.SetKeyValue(t, key, value)
|
||||||
|
tmc.SetN(t, 1)
|
||||||
|
tmc.Reset(nil)
|
||||||
|
tmc.GotN(t, 0)
|
||||||
|
tmc.SetN(t, 0)
|
||||||
|
if got := tmc.Get(key); got != "" {
|
||||||
|
t.Error("TextMapCarrier.Reset() failed to clear data")
|
||||||
|
}
|
||||||
|
}
|
115
sdk/internal/internaltest/text_map_propagator.go
Normal file
115
sdk/internal/internaltest/text_map_propagator.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator.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 internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ctxKeyType string
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
Injections uint64
|
||||||
|
Extractions uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newState(encoded string) state {
|
||||||
|
if encoded == "" {
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
s0, s1, _ := strings.Cut(encoded, ",")
|
||||||
|
injects, _ := strconv.ParseUint(s0, 10, 64)
|
||||||
|
extracts, _ := strconv.ParseUint(s1, 10, 64)
|
||||||
|
return state{
|
||||||
|
Injections: injects,
|
||||||
|
Extractions: extracts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s state) String() string {
|
||||||
|
return fmt.Sprintf("%d,%d", s.Injections, s.Extractions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMapPropagator is a propagation.TextMapPropagator used for testing.
|
||||||
|
type TextMapPropagator struct {
|
||||||
|
name string
|
||||||
|
ctxKey ctxKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ propagation.TextMapPropagator = (*TextMapPropagator)(nil)
|
||||||
|
|
||||||
|
// NewTextMapPropagator returns a new TextMapPropagator for testing. It will
|
||||||
|
// use name as the key it injects into a TextMapCarrier when Inject is called.
|
||||||
|
func NewTextMapPropagator(name string) *TextMapPropagator {
|
||||||
|
return &TextMapPropagator{name: name, ctxKey: ctxKeyType(name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromContext(ctx context.Context) state {
|
||||||
|
if v := ctx.Value(p.ctxKey); v != nil {
|
||||||
|
if s, ok := v.(state); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextMapPropagator) stateFromCarrier(carrier propagation.TextMapCarrier) state {
|
||||||
|
return newState(carrier.Get(p.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject sets cross-cutting concerns for p from ctx into carrier.
|
||||||
|
func (p *TextMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
|
||||||
|
s := p.stateFromContext(ctx)
|
||||||
|
s.Injections++
|
||||||
|
carrier.Set(p.name, s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectedN tests if p has made n injections to carrier.
|
||||||
|
func (p *TextMapPropagator) InjectedN(t *testing.T, carrier *TextMapCarrier, n int) bool {
|
||||||
|
if actual := p.stateFromCarrier(carrier).Injections; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} injected %d times, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract reads cross-cutting concerns for p from carrier into ctx.
|
||||||
|
func (p *TextMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
|
||||||
|
s := p.stateFromCarrier(carrier)
|
||||||
|
s.Extractions++
|
||||||
|
return context.WithValue(ctx, p.ctxKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractedN tests if p has made n extractions from the lineage of ctx.
|
||||||
|
// nolint (context is not first arg)
|
||||||
|
func (p *TextMapPropagator) ExtractedN(t *testing.T, ctx context.Context, n int) bool {
|
||||||
|
if actual := p.stateFromContext(ctx).Extractions; actual != uint64(n) {
|
||||||
|
t.Errorf("TextMapPropagator{%q} extracted %d time, not %d", p.name, actual, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns the name of p as the key who's value is set with Inject.
|
||||||
|
func (p *TextMapPropagator) Fields() []string { return []string{p.name} }
|
72
sdk/internal/internaltest/text_map_propagator_test.go
Normal file
72
sdk/internal/internaltest/text_map_propagator_test.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/internaltest/text_map_propagator_test.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 internaltest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextMapPropagatorInjectExtract(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
ctx := context.Background()
|
||||||
|
carrier := NewTextMapCarrier(map[string]string{name: value})
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
|
||||||
|
propagator.Inject(ctx, carrier)
|
||||||
|
// Carrier value overridden with state.
|
||||||
|
if carrier.SetKeyValue(t, name, "1,0") {
|
||||||
|
// Ensure nothing has been extracted yet.
|
||||||
|
propagator.ExtractedN(t, ctx, 0)
|
||||||
|
// Test the injection was counted.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = propagator.Extract(ctx, carrier)
|
||||||
|
v := ctx.Value(ctxKeyType(name))
|
||||||
|
if v == nil {
|
||||||
|
t.Error("TextMapPropagator.Extract failed to extract state")
|
||||||
|
}
|
||||||
|
if s, ok := v.(state); !ok {
|
||||||
|
t.Error("TextMapPropagator.Extract did not extract proper state")
|
||||||
|
} else if s.Extractions != 1 {
|
||||||
|
t.Error("TextMapPropagator.Extract did not increment state.Extractions")
|
||||||
|
}
|
||||||
|
if carrier.GotKey(t, name) {
|
||||||
|
// Test the extraction was counted.
|
||||||
|
propagator.ExtractedN(t, ctx, 1)
|
||||||
|
// Ensure no additional injection was recorded.
|
||||||
|
propagator.InjectedN(t, carrier, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextMapPropagatorFields(t *testing.T) {
|
||||||
|
name := "testing"
|
||||||
|
propagator := NewTextMapPropagator(name)
|
||||||
|
if got := propagator.Fields(); len(got) != 1 {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %d fields, want 1", len(got))
|
||||||
|
} else if got[0] != name {
|
||||||
|
t.Errorf("TextMapPropagator.Fields returned %q, want %q", got[0], name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewStateEmpty(t *testing.T) {
|
||||||
|
if want, got := (state{}), newState(""); got != want {
|
||||||
|
t.Errorf("newState(\"\") returned %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
|
||||||
"go.opentelemetry.io/otel/sdk"
|
"go.opentelemetry.io/otel/sdk"
|
||||||
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
|
|
||||||
"github.com/go-logr/logr/funcr"
|
"github.com/go-logr/logr/funcr"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
|
||||||
"go.opentelemetry.io/otel/sdk/internal/env"
|
"go.opentelemetry.io/otel/sdk/internal/env"
|
||||||
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@ import (
|
|||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
|
||||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||||
|
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
@ -1198,7 +1198,7 @@ func TestRecordError(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
err: ottest.NewTestError("test error"),
|
err: ottest.NewTestError("test error"),
|
||||||
typ: "go.opentelemetry.io/otel/internal/internaltest.TestError",
|
typ: "go.opentelemetry.io/otel/sdk/internal/internaltest.TestError",
|
||||||
msg: "test error",
|
msg: "test error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1250,7 +1250,7 @@ func TestRecordError(t *testing.T) {
|
|||||||
|
|
||||||
func TestRecordErrorWithStackTrace(t *testing.T) {
|
func TestRecordErrorWithStackTrace(t *testing.T) {
|
||||||
err := ottest.NewTestError("test error")
|
err := ottest.NewTestError("test error")
|
||||||
typ := "go.opentelemetry.io/otel/internal/internaltest.TestError"
|
typ := "go.opentelemetry.io/otel/sdk/internal/internaltest.TestError"
|
||||||
msg := "test error"
|
msg := "test error"
|
||||||
|
|
||||||
te := NewTestExporter()
|
te := NewTestExporter()
|
||||||
|
Reference in New Issue
Block a user