Skip to content

Commit 0338a66

Browse files
committed
Improve testability of the EnvVarHeaders struct and covered with tests.
1 parent f207dab commit 0338a66

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

Sources/Exporters/OpenTelemetryProtocol/common/EnvVarHeaders.swift

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,37 @@ import OpenTelemetryApi
88

99
/// Provides a framework for detection of resource information from the environment variable
1010
public struct EnvVarHeaders {
11-
private static let otelAttributesEnv = "OTEL_EXPORTER_OTLP_HEADERS"
1211
private static let labelListSplitter = Character(",")
1312
private static let labelKeyValueSplitter = Character("=")
1413

1514
/// This resource information is loaded from the
1615
/// environment variable.
17-
public static let attributes : [(String,String)]? = parseAttributes(rawEnvAttributes: ProcessInfo.processInfo.environment[otelAttributesEnv])
16+
public static let attributes : [(String,String)]? = EnvVarHeaders.attributes()
17+
18+
public static func attributes(for rawEnvAttributes: String? = ProcessInfo.processInfo.environment["OTEL_EXPORTER_OTLP_HEADERS"]) -> [(String,String)]? {
19+
parseAttributes(rawEnvAttributes: rawEnvAttributes)
20+
}
1821

1922
private init() {}
2023

24+
private static func isKey(token: String) -> Bool {
25+
let alpha = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
26+
let digit = CharacterSet(charactersIn: "0123456789")
27+
let special = CharacterSet(charactersIn: "!#$%&'*+-.^_`|~")
28+
let tchar = special.union(alpha).union(digit)
29+
return tchar.isSuperset(of: CharacterSet(charactersIn: token))
30+
}
31+
32+
private static func isValue(baggage: String) -> Bool {
33+
let asciiSet = CharacterSet(charactersIn: UnicodeScalar(0) ..< UnicodeScalar(0x80))
34+
let special = CharacterSet(charactersIn: "^\"|\"$")
35+
let baggageOctet = asciiSet.subtracting(.controlCharacters).subtracting(.whitespaces).union(special)
36+
return baggageOctet.isSuperset(of: CharacterSet(charactersIn: baggage))
37+
}
38+
2139
/// Creates a label map from the environment variable string.
2240
/// - Parameter rawEnvLabels: the comma-separated list of labels
41+
/// NOTE: Parsing does not fully match W3C Correlation-Context
2342
private static func parseAttributes(rawEnvAttributes: String?) -> [(String, String)]? {
2443
guard let rawEnvLabels = rawEnvAttributes else { return nil }
2544

@@ -30,10 +49,17 @@ public struct EnvVarHeaders {
3049
if split.count != 2 {
3150
return
3251
}
52+
3353
let key = split[0].trimmingCharacters(in: .whitespaces)
34-
let value = split[1].trimmingCharacters(in: CharacterSet(charactersIn: "^\"|\"$"))
54+
guard isKey(token: key) else { return }
55+
56+
let value = split[1].trimmingCharacters(in: .whitespaces)
57+
guard isValue(baggage: value) else { return }
58+
3559
labels.append((key,value))
3660
}
37-
return labels
61+
return labels.count > 0 ? labels : nil
3862
}
3963
}
64+
65+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import Foundation
7+
import OpenTelemetryProtocolExporter
8+
import XCTest
9+
10+
class EnvVarHeadersTests: XCTestCase {
11+
12+
func test_attributesIsNil_whenAccessedThroughStaticProperty() throws {
13+
XCTAssertNil(EnvVarHeaders.attributes)
14+
}
15+
16+
func test_attributesIsNil_whenNoRawValueProvided() throws {
17+
XCTAssertNil(EnvVarHeaders.attributes(for: nil))
18+
}
19+
20+
func test_attributesContainOneKeyValuePair_whenRawValueProvidedHasOneKeyValuePair() throws {
21+
let capturedAttributes = EnvVarHeaders.attributes(for: "key1=value1")
22+
XCTAssertNotNil(capturedAttributes)
23+
XCTAssertEqual(capturedAttributes![0].0, "key1")
24+
XCTAssertEqual(capturedAttributes![0].1, "value1")
25+
}
26+
27+
func test_attributesIsNil_whenInvalidRawValueProvided() throws {
28+
XCTAssertNil(EnvVarHeaders.attributes(for: "key1"))
29+
}
30+
31+
func test_attributesContainTwoKeyValuePair_whenRawValueProvidedHasTwoKeyValuePair() throws {
32+
let capturedAttributes = EnvVarHeaders.attributes(for: " key1=value1, key2 = value2 ")
33+
XCTAssertNotNil(capturedAttributes)
34+
XCTAssertEqual(capturedAttributes![0].0, "key1")
35+
XCTAssertEqual(capturedAttributes![0].1, "value1")
36+
XCTAssertEqual(capturedAttributes![1].0, "key2")
37+
XCTAssertEqual(capturedAttributes![1].1, "value2")
38+
}
39+
40+
func test_attributesIsNil_whenRawValueContainsWhiteSpace() throws {
41+
let capturedAttributes = EnvVarHeaders.attributes(for: "key=value with\twhitespace")
42+
XCTAssertNil(capturedAttributes)
43+
}
44+
45+
func test_attributesExcludesInvalidTuples_whenRawValueContainsInvalidCharacters() throws {
46+
let capturedAttributes = EnvVarHeaders.attributes(for: "key=value with whitespace")
47+
XCTAssertNil(capturedAttributes)
48+
}
49+
50+
}

0 commit comments

Comments
 (0)