Repository: open-telemetry/opentelemetry-go
Pull request #1748 (2021-03-29 18:16:16)

Update SpanSnapshot to use parent SpanContext
Having only the parent span ID and a separate field to communicate if the parent was remote does not provide a comprehensive view of the parent span nor is it an efficient way to transmit this information. Update the SpanSnapshot to have a `Parent` field that contains the parent span context. This field replaces the ParentSpanID and HasRemoteParent fields.

Additionally to this being an improvement to the functionality of the SDK, it also helps move towards having the `SpanSnapshot` implement the [`ReadonlySpan`](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.1.0/specification/trace/sdk.md#additional-span-interfaces) interface.

Part of https://github.com/open-telemetry/opentelemetry-go/issues/1380

codecov[bot]: # [Codecov](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748?src=pr&el=h1) Report
> Merging [#1748](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748?src=pr&el=desc) (ac0c90c) into [main](https://codecov.io/gh/open-telemetry/opentelemetry-go/commit/604b05cbcf5796c85794272456448c1d0d081547?el=desc) (604b05c) will **increase** coverage by `0.0%`.
> The diff coverage is `100.0%`.

[![Impacted file tree graph](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/graphs/tree.svg?width=650&height=150&src=pr&token=8efTmh4kvf)](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##            main   #1748   +/-   ##
=====================================
  Coverage   78.0%   78.0%           
=====================================
  Files        132     132           
  Lines       6941    6943    +2     
=====================================
+ Hits        5417    5421    +4     
  Misses      1274    1274           
+ Partials     250     248    -2     
```


| [Impacted Files](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [exporters/otlp/internal/otlptest/data.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-ZXhwb3J0ZXJzL290bHAvaW50ZXJuYWwvb3RscHRlc3QvZGF0YS5nbw==) | `64.0% <100.0%> (+2.2%)` | :arrow_up: |
| [exporters/otlp/internal/transform/span.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-ZXhwb3J0ZXJzL290bHAvaW50ZXJuYWwvdHJhbnNmb3JtL3NwYW4uZ28=) | `98.1% <100.0%> (ø)` | |
| [exporters/trace/jaeger/jaeger.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-ZXhwb3J0ZXJzL3RyYWNlL2phZWdlci9qYWVnZXIuZ28=) | `93.5% <100.0%> (+<0.1%)` | :arrow_up: |
| [exporters/trace/zipkin/model.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-ZXhwb3J0ZXJzL3RyYWNlL3ppcGtpbi9tb2RlbC5nbw==) | `98.6% <100.0%> (ø)` | |
| [sdk/trace/span.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-c2RrL3RyYWNlL3NwYW4uZ28=) | `90.3% <100.0%> (-0.1%)` | :arrow_down: |
| [exporters/otlp/otlpgrpc/connection.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-ZXhwb3J0ZXJzL290bHAvb3RscGdycGMvY29ubmVjdGlvbi5nbw==) | `86.9% <0.0%> (-1.9%)` | :arrow_down: |
| [sdk/metric/sdk.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-c2RrL21ldHJpYy9zZGsuZ28=) | `81.3% <0.0%> (+1.1%)` | :arrow_up: |
| [sdk/metric/refcount\_mapped.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1748/diff?src=pr&el=tree#diff-c2RrL21ldHJpYy9yZWZjb3VudF9tYXBwZWQuZ28=) | `100.0% <0.0%> (+22.2%)` | :arrow_up: |

MrAlias: @paivagustavo there is [this issue](https://github.com/open-telemetry/opentelemetry-go/issues/1727) which is being addressed in [this PR](https://github.com/open-telemetry/opentelemetry-go/pull/1749), but if you see other places where it needs to be refactored please do indeed open an issue or PR.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 226f05bf..6eb237bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - The storage of a local or remote Span in a `context.Context` using its SpanContext is unified to store just the current Span.
   The Span's SpanContext can now self-identify as being remote or not.
   This means that `"go.opentelemetry.io/otel/trace".ContextWithRemoteSpanContext` will now overwrite any existing current Span, not just existing remote Spans, and make it the current Span in a `context.Context`. (#1731)
+- Information about a parent span context in a `"go.opentelemetry.io/otel/export/trace".SpanSnapshot` is unified in a new `Parent` field.
+  The existing `ParentSpanID` and `HasRemoteParent` fields are removed in favor of this. (#1748)
 
 ### Removed
 
diff --git a/exporters/otlp/internal/transform/span.go b/exporters/otlp/internal/transform/span.go
index 6cda57e7..a4ffc4c1 100644
--- a/exporters/otlp/internal/transform/span.go
+++ b/exporters/otlp/internal/transform/span.go
@@ -121,8 +121,8 @@ func span(sd *export.SpanSnapshot) *tracepb.Span {
 		DroppedLinksCount:      uint32(sd.DroppedLinkCount),
 	}
 
-	if sd.ParentSpanID.IsValid() {
-		s.ParentSpanId = sd.ParentSpanID[:]
+	if psid := sd.Parent.SpanID(); psid.IsValid() {
+		s.ParentSpanId = psid[:]
 	}
 
 	return s
diff --git a/exporters/trace/jaeger/jaeger.go b/exporters/trace/jaeger/jaeger.go
index 11b3e5d9..b1b9960d 100644
--- a/exporters/trace/jaeger/jaeger.go
+++ b/exporters/trace/jaeger/jaeger.go
@@ -314,11 +314,12 @@ func spanSnapshotToThrift(ss *export.SpanSnapshot) *gen.Span {
 
 	tid := ss.SpanContext.TraceID()
 	sid := ss.SpanContext.SpanID()
+	psid := ss.Parent.SpanID()
 	return &gen.Span{
 		TraceIdHigh:   int64(binary.BigEndian.Uint64(tid[0:8])),
 		TraceIdLow:    int64(binary.BigEndian.Uint64(tid[8:16])),
 		SpanId:        int64(binary.BigEndian.Uint64(sid[:])),
-		ParentSpanId:  int64(binary.BigEndian.Uint64(ss.ParentSpanID[:])),
+		ParentSpanId:  int64(binary.BigEndian.Uint64(psid[:])),
 		OperationName: ss.Name, // TODO: if span kind is added then add prefix "Sent"/"Recv"
 		Flags:         int32(ss.SpanContext.TraceFlags()),
 		StartTime:     ss.StartTime.UnixNano() / 1000,
diff --git a/exporters/trace/zipkin/model.go b/exporters/trace/zipkin/model.go
index 26df037f..b54048f3 100644
--- a/exporters/trace/zipkin/model.go
+++ b/exporters/trace/zipkin/model.go
@@ -72,7 +72,7 @@ func toZipkinSpanContext(data *export.SpanSnapshot) zkmodel.SpanContext {
 	return zkmodel.SpanContext{
 		TraceID:  toZipkinTraceID(data.SpanContext.TraceID()),
 		ID:       toZipkinID(data.SpanContext.SpanID()),
-		ParentID: toZipkinParentID(data.ParentSpanID),
+		ParentID: toZipkinParentID(data.Parent.SpanID()),
 		Debug:    false,
 		Sampled:  nil,
 		Err:      nil,
diff --git a/sdk/export/trace/trace.go b/sdk/export/trace/trace.go
index 28fcb368..66e1826a 100644
--- a/sdk/export/trace/trace.go
+++ b/sdk/export/trace/trace.go
@@ -54,20 +54,19 @@ type SpanExporter interface {
 // SpanSnapshot should be treated as immutable. Changes to the span from which
 // the SpanSnapshot was created are NOT reflected in the SpanSnapshot.
 type SpanSnapshot struct {
-	SpanContext  trace.SpanContext
-	ParentSpanID trace.SpanID
-	SpanKind     trace.SpanKind
-	Name         string
-	StartTime    time.Time
+	SpanContext trace.SpanContext
+	Parent      trace.SpanContext
+	SpanKind    trace.SpanKind
+	Name        string
+	StartTime   time.Time
 	// The wall clock time of EndTime will be adjusted to always be offset
 	// from StartTime by the duration of the span.
-	EndTime         time.Time
-	Attributes      []attribute.KeyValue
-	MessageEvents   []trace.Event
-	Links           []trace.Link
-	StatusCode      codes.Code
-	StatusMessage   string
-	HasRemoteParent bool
+	EndTime       time.Time
+	Attributes    []attribute.KeyValue
+	MessageEvents []trace.Event
+	Links         []trace.Link
+	StatusCode    codes.Code
+	StatusMessage string
 
 	// DroppedAttributeCount contains dropped attributes for the span itself, events and links.
 	DroppedAttributeCount    int
diff --git a/sdk/trace/span.go b/sdk/trace/span.go
index cbf37769..15e8402e 100644
--- a/sdk/trace/span.go
+++ b/sdk/trace/span.go
@@ -443,10 +443,9 @@ func (s *span) Snapshot() *export.SpanSnapshot {
 
 	sd.ChildSpanCount = s.childSpanCount
 	sd.EndTime = s.endTime
-	sd.HasRemoteParent = s.parent.IsRemote()
 	sd.InstrumentationLibrary = s.instrumentationLibrary
 	sd.Name = s.name
-	sd.ParentSpanID = s.parent.SpanID()
+	sd.Parent = s.parent
 	sd.Resource = s.resource
 	sd.SpanContext = s.spanContext
 	sd.SpanKind = s.spanKind


Test patch:
diff --git a/exporters/otlp/internal/otlptest/data.go b/exporters/otlp/internal/otlptest/data.go
index 354e6c43..40eac27c 100644
--- a/exporters/otlp/internal/otlptest/data.go
+++ b/exporters/otlp/internal/otlptest/data.go
@@ -87,7 +87,11 @@ func SingleSpanSnapshot() []*exporttrace.SpanSnapshot {
 			SpanID:     trace.SpanID{3, 4, 5, 6, 7, 8, 9, 0},
 			TraceFlags: trace.FlagsSampled,
 		}),
-		ParentSpanID:             trace.SpanID{1, 2, 3, 4, 5, 6, 7, 8},
+		Parent: trace.NewSpanContext(trace.SpanContextConfig{
+			TraceID:    trace.TraceID{2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9},
+			SpanID:     trace.SpanID{1, 2, 3, 4, 5, 6, 7, 8},
+			TraceFlags: trace.FlagsSampled,
+		}),
 		SpanKind:                 trace.SpanKindInternal,
 		Name:                     "foo",
 		StartTime:                time.Date(2020, time.December, 8, 20, 23, 0, 0, time.UTC),
@@ -97,7 +101,6 @@ func SingleSpanSnapshot() []*exporttrace.SpanSnapshot {
 		Links:                    []trace.Link{},
 		StatusCode:               codes.Ok,
 		StatusMessage:            "",
-		HasRemoteParent:          false,
 		DroppedAttributeCount:    0,
 		DroppedMessageEventCount: 0,
 		DroppedLinkCount:         0,
diff --git a/exporters/otlp/internal/transform/span_test.go b/exporters/otlp/internal/transform/span_test.go
index e64308ef..bc2998e7 100644
--- a/exporters/otlp/internal/transform/span_test.go
+++ b/exporters/otlp/internal/transform/span_test.go
@@ -205,11 +205,16 @@ func TestSpanData(t *testing.T) {
 			SpanID:     trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			TraceState: traceState,
 		}),
-		SpanKind:     trace.SpanKindServer,
-		ParentSpanID: trace.SpanID{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8},
-		Name:         "span data to span data",
-		StartTime:    startTime,
-		EndTime:      endTime,
+		Parent: trace.NewSpanContext(trace.SpanContextConfig{
+			TraceID:    trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+			SpanID:     trace.SpanID{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8},
+			TraceState: traceState,
+			Remote:     true,
+		}),
+		SpanKind:  trace.SpanKindServer,
+		Name:      "span data to span data",
+		StartTime: startTime,
+		EndTime:   endTime,
 		MessageEvents: []trace.Event{
 			{Time: startTime,
 				Attributes: []attribute.KeyValue{
@@ -244,9 +249,8 @@ func TestSpanData(t *testing.T) {
 				},
 			},
 		},
-		StatusCode:      codes.Error,
-		StatusMessage:   "utterly unrecognized",
-		HasRemoteParent: true,
+		StatusCode:    codes.Error,
+		StatusMessage: "utterly unrecognized",
 		Attributes: []attribute.KeyValue{
 			attribute.Int64("timeout_ns", 12e9),
 		},
diff --git a/exporters/otlp/otlp_span_test.go b/exporters/otlp/otlp_span_test.go
index 62fdc39e..7e71df21 100644
--- a/exporters/otlp/otlp_span_test.go
+++ b/exporters/otlp/otlp_span_test.go
@@ -104,11 +104,15 @@ func TestExportSpans(t *testing.T) {
 						SpanID:     trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 2}),
 						TraceFlags: byte(1),
 					}),
-					ParentSpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
-					SpanKind:     trace.SpanKindInternal,
-					Name:         "internal process",
-					StartTime:    startTime,
-					EndTime:      endTime,
+					Parent: trace.NewSpanContext(trace.SpanContextConfig{
+						TraceID:    trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}),
+						SpanID:     trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
+						TraceFlags: byte(1),
+					}),
+					SpanKind:  trace.SpanKindInternal,
+					Name:      "internal process",
+					StartTime: startTime,
+					EndTime:   endTime,
 					Attributes: []attribute.KeyValue{
 						attribute.String("user", "alice"),
 						attribute.Bool("authenticated", true),
diff --git a/exporters/stdout/trace_test.go b/exporters/stdout/trace_test.go
index 851bef03..7c0f7e91 100644
--- a/exporters/stdout/trace_test.go
+++ b/exporters/stdout/trace_test.go
@@ -84,7 +84,13 @@ func TestExporter_ExportSpan(t *testing.T) {
 		`"Key":"key",` +
 		`"Value":{"Type":"STRING","Value":"val"}` +
 		`}],"Remote":false},` +
-		`"ParentSpanID":"0000000000000000",` +
+		`"Parent":{` +
+		`"TraceID":"00000000000000000000000000000000",` +
+		`"SpanID":"0000000000000000",` +
+		`"TraceFlags":0,` +
+		`"TraceState":null,` +
+		`"Remote":false` +
+		`},` +
 		`"SpanKind":1,` +
 		`"Name":"/foo",` +
 		`"StartTime":` + string(expectedSerializedNow) + "," +
@@ -123,7 +129,6 @@ func TestExporter_ExportSpan(t *testing.T) {
 		`"Links":null,` +
 		`"StatusCode":"Error",` +
 		`"StatusMessage":"interesting",` +
-		`"HasRemoteParent":false,` +
 		`"DroppedAttributeCount":0,` +
 		`"DroppedMessageEventCount":0,` +
 		`"DroppedLinkCount":0,` +
diff --git a/exporters/trace/jaeger/jaeger_test.go b/exporters/trace/jaeger/jaeger_test.go
index 0226d79a..a107af00 100644
--- a/exporters/trace/jaeger/jaeger_test.go
+++ b/exporters/trace/jaeger/jaeger_test.go
@@ -445,11 +445,14 @@ func Test_spanSnapshotToThrift(t *testing.T) {
 		{
 			name: "with parent",
 			data: &export.SpanSnapshot{
-				ParentSpanID: parentSpanID,
 				SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
 					TraceID: traceID,
 					SpanID:  spanID,
 				}),
+				Parent: trace.NewSpanContext(trace.SpanContextConfig{
+					TraceID: traceID,
+					SpanID:  parentSpanID,
+				}),
 				Links: []trace.Link{
 					{
 						SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
@@ -499,11 +502,14 @@ func Test_spanSnapshotToThrift(t *testing.T) {
 		{
 			name: "resources do not affect the tags",
 			data: &export.SpanSnapshot{
-				ParentSpanID: parentSpanID,
 				SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
 					TraceID: traceID,
 					SpanID:  spanID,
 				}),
+				Parent: trace.NewSpanContext(trace.SpanContextConfig{
+					TraceID: traceID,
+					SpanID:  parentSpanID,
+				}),
 				Name:      "/foo",
 				StartTime: now,
 				EndTime:   now,
diff --git a/exporters/trace/zipkin/model_test.go b/exporters/trace/zipkin/model_test.go
index e4575728..77e4e999 100644
--- a/exporters/trace/zipkin/model_test.go
+++ b/exporters/trace/zipkin/model_test.go
@@ -46,11 +46,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindServer,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindServer,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -80,11 +83,10 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{},
-			SpanKind:     trace.SpanKindServer,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			SpanKind:  trace.SpanKindServer,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -113,11 +115,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindUnspecified,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindUnspecified,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -146,11 +151,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindInternal,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindInternal,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -179,11 +187,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindClient,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindClient,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -212,11 +223,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindProducer,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindProducer,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -245,11 +259,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindConsumer,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindConsumer,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -278,11 +295,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindServer,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindServer,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.Int64("attr1", 42),
 				attribute.String("attr2", "bar"),
@@ -298,11 +318,14 @@ func TestModelConversion(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
-			SpanKind:     trace.SpanKindServer,
-			Name:         "foo",
-			StartTime:    time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
-			EndTime:      time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38},
+			}),
+			SpanKind:  trace.SpanKindServer,
+			Name:      "foo",
+			StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
+			EndTime:   time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC),
 			Attributes: []attribute.KeyValue{
 				attribute.String("error", "false"),
 			},
diff --git a/exporters/trace/zipkin/zipkin_test.go b/exporters/trace/zipkin/zipkin_test.go
index e45cf91e..07748a87 100644
--- a/exporters/trace/zipkin/zipkin_test.go
+++ b/exporters/trace/zipkin/zipkin_test.go
@@ -241,7 +241,6 @@ func TestExportSpans(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
 			}),
-			ParentSpanID:  trace.SpanID{},
 			SpanKind:      trace.SpanKindServer,
 			Name:          "foo",
 			StartTime:     time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC),
@@ -258,7 +257,10 @@ func TestExportSpans(t *testing.T) {
 				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
 				SpanID:  trace.SpanID{0xDF, 0xDE, 0xDD, 0xDC, 0xDB, 0xDA, 0xD9, 0xD8},
 			}),
-			ParentSpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
+			Parent: trace.NewSpanContext(trace.SpanContextConfig{
+				TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
+				SpanID:  trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
+			}),
 			SpanKind:      trace.SpanKindServer,
 			Name:          "bar",
 			StartTime:     time.Date(2020, time.March, 11, 19, 24, 15, 0, time.UTC),
diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go
index 1bb05b41..3eee3c36 100644
--- a/sdk/trace/trace_test.go
+++ b/sdk/trace/trace_test.go
@@ -58,6 +58,7 @@ func (s *storingHandler) Reset() {
 var (
 	tid trace.TraceID
 	sid trace.SpanID
+	sc  trace.SpanContext
 
 	handler *storingHandler = &storingHandler{}
 
@@ -68,6 +69,11 @@ var (
 func init() {
 	tid, _ = trace.TraceIDFromHex("01020304050607080102040810203040")
 	sid, _ = trace.SpanIDFromHex("0102040810203040")
+	sc = trace.NewSpanContext(trace.SpanContextConfig{
+		TraceID:    tid,
+		SpanID:     sid,
+		TraceFlags: 0x1,
+	})
 
 	k1 = attribute.Key("k1")
 	kv1 = k1.String("v1")
@@ -314,18 +320,13 @@ func TestStartSpanWithParent(t *testing.T) {
 	tr := tp.Tracer("SpanWithParent")
 	ctx := context.Background()
 
-	sc1 := trace.NewSpanContext(trace.SpanContextConfig{
-		TraceID:    tid,
-		SpanID:     sid,
-		TraceFlags: 0x1,
-	})
-	_, s1 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc1), "span1-unsampled-parent1")
-	if err := checkChild(t, sc1, s1); err != nil {
+	_, s1 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span1-unsampled-parent1")
+	if err := checkChild(t, sc, s1); err != nil {
 		t.Error(err)
 	}
 
-	_, s2 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc1), "span2-unsampled-parent1")
-	if err := checkChild(t, sc1, s2); err != nil {
+	_, s2 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span2-unsampled-parent1")
+	if err := checkChild(t, sc, s2); err != nil {
 		t.Error(err)
 	}
 
@@ -333,12 +334,7 @@ func TestStartSpanWithParent(t *testing.T) {
 	if err != nil {
 		t.Error(err)
 	}
-	sc2 := trace.NewSpanContext(trace.SpanContextConfig{
-		TraceID:    tid,
-		SpanID:     sid,
-		TraceFlags: 0x1,
-		TraceState: ts,
-	})
+	sc2 := sc.WithTraceState(ts)
 	_, s3 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc2), "span3-sampled-parent2")
 	if err := checkChild(t, sc2, s3); err != nil {
 		t.Error(err)
@@ -374,14 +370,13 @@ func TestSetSpanAttributesOnStart(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		Attributes: []attribute.KeyValue{
 			attribute.String("key1", "value1"),
 			attribute.String("key2", "value2"),
 		},
 		SpanKind:               trace.SpanKindInternal,
-		HasRemoteParent:        true,
 		InstrumentationLibrary: instrumentation.Library{Name: "StartSpanAttribute"},
 	}
 	if diff := cmpDiff(got, want); diff != "" {
@@ -404,13 +399,12 @@ func TestSetSpanAttributes(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		Attributes: []attribute.KeyValue{
 			attribute.String("key1", "value1"),
 		},
 		SpanKind:               trace.SpanKindInternal,
-		HasRemoteParent:        true,
 		InstrumentationLibrary: instrumentation.Library{Name: "SpanAttribute"},
 	}
 	if diff := cmpDiff(got, want); diff != "" {
@@ -418,8 +412,6 @@ func TestSetSpanAttributes(t *testing.T) {
 	}
 }
 
-// Test that the sampler is called for local child spans. This is verified by checking
-// that the attributes set in the sampler are set on the child span.
 func TestSamplerAttributesLocalChildSpan(t *testing.T) {
 	sampler := &testSampler{prefix: "span", t: t}
 	te := NewTestExporter()
@@ -433,55 +425,13 @@ func TestSamplerAttributesLocalChildSpan(t *testing.T) {
 	span.End()
 
 	got := te.Spans()
-
-	// endSpan expects only a single span in the test exporter, so manually clear the
-	// fields that can't be tested for easily (times, span and trace ids).
-	pid := got[0].SpanContext.SpanID()
-	got[0].SpanContext = got[0].SpanContext.WithTraceID(tid)
-	got[0].ParentSpanID = sid
-
-	checkTime(&got[0].StartTime)
-	checkTime(&got[0].EndTime)
-
-	got[1].SpanContext = got[1].SpanContext.WithSpanID(trace.SpanID{})
-	got[1].SpanContext = got[1].SpanContext.WithTraceID(tid)
-	got[1].ParentSpanID = pid
-	got[0].SpanContext = got[0].SpanContext.WithSpanID(trace.SpanID{})
-
-	checkTime(&got[1].StartTime)
-	checkTime(&got[1].EndTime)
-
-	want := []*export.SpanSnapshot{
-		{
-			SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
-				TraceID:    tid,
-				TraceFlags: 0x1,
-			}),
-			ParentSpanID:           sid,
-			Name:                   "span1",
-			Attributes:             []attribute.KeyValue{attribute.Int("callCount", 2)},
-			SpanKind:               trace.SpanKindInternal,
-			HasRemoteParent:        false,
-			InstrumentationLibrary: instrumentation.Library{Name: "SpanTwo"},
-		},
-		{
-			SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
-				TraceID:    tid,
-				TraceFlags: 0x1,
-			}),
-			ParentSpanID:           pid,
-			Name:                   "span0",
-			Attributes:             []attribute.KeyValue{attribute.Int("callCount", 1)},
-			SpanKind:               trace.SpanKindInternal,
-			HasRemoteParent:        false,
-			ChildSpanCount:         1,
-			InstrumentationLibrary: instrumentation.Library{Name: "SpanOne"},
-		},
-	}
-
-	if diff := cmpDiff(got, want); diff != "" {
-		t.Errorf("SetSpanAttributesLocalChildSpan: -got +want %s", diff)
-	}
+	require.Len(t, got, 2)
+	// FILO order above means spanTwo <-> gotSpan0 and span <-> gotSpan1.
+	gotSpan0, gotSpan1 := got[0], got[1]
+	// Ensure sampler is called for local child spans by verifying the
+	// attributes set by the sampler are set on the child span.
+	assert.Equal(t, []attribute.KeyValue{attribute.Int("callCount", 2)}, gotSpan0.Attributes)
+	assert.Equal(t, []attribute.KeyValue{attribute.Int("callCount", 1)}, gotSpan1.Attributes)
 }
 
 func TestSetSpanAttributesOverLimit(t *testing.T) {
@@ -505,14 +455,13 @@ func TestSetSpanAttributesOverLimit(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		Attributes: []attribute.KeyValue{
 			attribute.Bool("key1", false),
 			attribute.Int64("key4", 4),
 		},
 		SpanKind:               trace.SpanKindInternal,
-		HasRemoteParent:        true,
 		DroppedAttributeCount:  1,
 		InstrumentationLibrary: instrumentation.Library{Name: "SpanAttributesOverLimit"},
 	}
@@ -540,13 +489,12 @@ func TestSetSpanAttributesWithInvalidKey(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		Attributes: []attribute.KeyValue{
 			attribute.Bool("key1", false),
 		},
 		SpanKind:               trace.SpanKindInternal,
-		HasRemoteParent:        true,
 		DroppedAttributeCount:  0,
 		InstrumentationLibrary: instrumentation.Library{Name: "SpanToSetInvalidKeyOrValue"},
 	}
@@ -585,9 +533,8 @@ func TestEvents(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:    sid,
-		Name:            "span0",
-		HasRemoteParent: true,
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		MessageEvents: []trace.Event{
 			{Name: "foo", Attributes: []attribute.KeyValue{k1v1}},
 			{Name: "bar", Attributes: []attribute.KeyValue{k2v2, k3v3}},
@@ -635,14 +582,13 @@ func TestEventsOverLimit(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		MessageEvents: []trace.Event{
 			{Name: "foo", Attributes: []attribute.KeyValue{k1v1}},
 			{Name: "bar", Attributes: []attribute.KeyValue{k2v2, k3v3}},
 		},
 		DroppedMessageEventCount: 2,
-		HasRemoteParent:          true,
 		SpanKind:                 trace.SpanKindInternal,
 		InstrumentationLibrary:   instrumentation.Library{Name: "EventsOverLimit"},
 	}
@@ -678,9 +624,8 @@ func TestLinks(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:           sid,
+		Parent:                 sc.WithRemote(true),
 		Name:                   "span0",
-		HasRemoteParent:        true,
 		Links:                  links,
 		SpanKind:               trace.SpanKindInternal,
 		InstrumentationLibrary: instrumentation.Library{Name: "Links"},
@@ -720,14 +665,13 @@ func TestLinksOverLimit(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		Links: []trace.Link{
 			{SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2}},
 			{SpanContext: sc3, Attributes: []attribute.KeyValue{k3v3}},
 		},
 		DroppedLinkCount:       1,
-		HasRemoteParent:        true,
 		SpanKind:               trace.SpanKindInternal,
 		InstrumentationLibrary: instrumentation.Library{Name: "LinksOverLimit"},
 	}
@@ -742,11 +686,7 @@ func TestSetSpanName(t *testing.T) {
 	ctx := context.Background()
 
 	want := "SpanName-1"
-	ctx = trace.ContextWithRemoteSpanContext(ctx, trace.NewSpanContext(trace.SpanContextConfig{
-		TraceID:    tid,
-		SpanID:     sid,
-		TraceFlags: 1,
-	}))
+	ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
 	_, span := tp.Tracer("SetSpanName").Start(ctx, "SpanName-1")
 	got, err := endSpan(te, span)
 	if err != nil {
@@ -774,12 +714,11 @@ func TestSetSpanStatus(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:           sid,
+		Parent:                 sc.WithRemote(true),
 		Name:                   "span0",
 		SpanKind:               trace.SpanKindInternal,
 		StatusCode:             codes.Error,
 		StatusMessage:          "Error",
-		HasRemoteParent:        true,
 		InstrumentationLibrary: instrumentation.Library{Name: "SpanStatus"},
 	}
 	if diff := cmpDiff(got, want); diff != "" {
@@ -803,12 +742,11 @@ func TestSetSpanStatusWithoutMessageWhenStatusIsNotError(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:           sid,
+		Parent:                 sc.WithRemote(true),
 		Name:                   "span0",
 		SpanKind:               trace.SpanKindInternal,
 		StatusCode:             codes.Ok,
 		StatusMessage:          "",
-		HasRemoteParent:        true,
 		InstrumentationLibrary: instrumentation.Library{Name: "SpanStatus"},
 	}
 	if diff := cmpDiff(got, want); diff != "" {
@@ -823,14 +761,6 @@ func cmpDiff(x, y interface{}) string {
 		cmp.AllowUnexported(trace.TraceState{}))
 }
 
-func remoteSpanContext() trace.SpanContext {
-	return trace.NewSpanContext(trace.SpanContextConfig{
-		TraceID:    tid,
-		SpanID:     sid,
-		TraceFlags: 1,
-	})
-}
-
 // checkChild is test utility function that tests that c has fields set appropriately,
 // given that it is a child span of p.
 func checkChild(t *testing.T, p trace.SpanContext, apiSpan trace.Span) error {
@@ -863,10 +793,8 @@ func startSpan(tp *TracerProvider, trName string, args ...trace.SpanOption) trac
 // context contains TraceFlags with sampled bit set. This allows the
 // span to be automatically sampled.
 func startNamedSpan(tp *TracerProvider, trName, name string, args ...trace.SpanOption) trace.Span {
-	ctx := context.Background()
-	ctx = trace.ContextWithRemoteSpanContext(ctx, remoteSpanContext())
 	_, span := tp.Tracer(trName).Start(
-		ctx,
+		trace.ContextWithRemoteSpanContext(context.Background(), sc),
 		name,
 		args...,
 	)
@@ -958,7 +886,7 @@ func TestStartSpanAfterEnd(t *testing.T) {
 	ctx := context.Background()
 
 	tr := tp.Tracer("SpanAfterEnd")
-	ctx, span0 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, remoteSpanContext()), "parent")
+	ctx, span0 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "parent")
 	ctx1, span1 := tr.Start(ctx, "span-1")
 	span1.End()
 	// Start a new span with the context containing span-1
@@ -989,10 +917,10 @@ func TestStartSpanAfterEnd(t *testing.T) {
 	if got, want := gotSpan2.SpanContext.TraceID(), gotParent.SpanContext.TraceID(); got != want {
 		t.Errorf("span-2.TraceID=%q; want %q", got, want)
 	}
-	if got, want := gotSpan1.ParentSpanID, gotParent.SpanContext.SpanID(); got != want {
+	if got, want := gotSpan1.Parent.SpanID(), gotParent.SpanContext.SpanID(); got != want {
 		t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want)
 	}
-	if got, want := gotSpan2.ParentSpanID, gotSpan1.SpanContext.SpanID(); got != want {
+	if got, want := gotSpan2.Parent.SpanID(), gotSpan1.SpanContext.SpanID(); got != want {
 		t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want)
 	}
 }
@@ -1160,11 +1088,10 @@ func TestRecordError(t *testing.T) {
 				TraceID:    tid,
 				TraceFlags: 0x1,
 			}),
-			ParentSpanID:    sid,
-			Name:            "span0",
-			StatusCode:      codes.Unset,
-			SpanKind:        trace.SpanKindInternal,
-			HasRemoteParent: true,
+			Parent:     sc.WithRemote(true),
+			Name:       "span0",
+			StatusCode: codes.Unset,
+			SpanKind:   trace.SpanKindInternal,
 			MessageEvents: []trace.Event{
 				{
 					Name: errorEventName,
@@ -1200,10 +1127,9 @@ func TestRecordErrorNil(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:           sid,
+		Parent:                 sc.WithRemote(true),
 		Name:                   "span0",
 		SpanKind:               trace.SpanKindInternal,
-		HasRemoteParent:        true,
 		StatusCode:             codes.Unset,
 		StatusMessage:          "",
 		InstrumentationLibrary: instrumentation.Library{Name: "RecordErrorNil"},
@@ -1298,13 +1224,12 @@ func TestWithResource(t *testing.T) {
 					TraceID:    tid,
 					TraceFlags: 0x1,
 				}),
-				ParentSpanID: sid,
-				Name:         "span0",
+				Parent: sc.WithRemote(true),
+				Name:   "span0",
 				Attributes: []attribute.KeyValue{
 					attribute.String("key1", "value1"),
 				},
 				SpanKind:               trace.SpanKindInternal,
-				HasRemoteParent:        true,
 				Resource:               tc.want,
 				InstrumentationLibrary: instrumentation.Library{Name: "WithResource"},
 			}
@@ -1320,7 +1245,7 @@ func TestWithInstrumentationVersion(t *testing.T) {
 	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
 
 	ctx := context.Background()
-	ctx = trace.ContextWithRemoteSpanContext(ctx, remoteSpanContext())
+	ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
 	_, span := tp.Tracer(
 		"WithInstrumentationVersion",
 		trace.WithInstrumentationVersion("v0.1.0"),
@@ -1335,10 +1260,9 @@ func TestWithInstrumentationVersion(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:    sid,
-		Name:            "span0",
-		SpanKind:        trace.SpanKindInternal,
-		HasRemoteParent: true,
+		Parent:   sc.WithRemote(true),
+		Name:     "span0",
+		SpanKind: trace.SpanKindInternal,
 		InstrumentationLibrary: instrumentation.Library{
 			Name:    "WithInstrumentationVersion",
 			Version: "v0.1.0",
@@ -1524,9 +1448,9 @@ func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID: sid,
-		Name:         "span0",
-		Attributes:   nil,
+		Parent:     sc.WithRemote(true),
+		Name:       "span0",
+		Attributes: nil,
 		MessageEvents: []trace.Event{
 			{
 				Name: "test1",
@@ -1544,7 +1468,6 @@ func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) {
 			},
 		},
 		SpanKind:               trace.SpanKindInternal,
-		HasRemoteParent:        true,
 		DroppedAttributeCount:  2,
 		InstrumentationLibrary: instrumentation.Library{Name: "AddSpanEventWithOverLimitedAttributes"},
 	}
@@ -1584,9 +1507,8 @@ func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) {
 			TraceID:    tid,
 			TraceFlags: 0x1,
 		}),
-		ParentSpanID:    sid,
-		Name:            "span0",
-		HasRemoteParent: true,
+		Parent: sc.WithRemote(true),
+		Name:   "span0",
 		Links: []trace.Link{
 			{SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1}},
 			{SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2}},