Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,34 +317,54 @@ sentrycrashjson_addFloatingPointElement(
if (isnan(value)) {
return sentrycrashjson_addNullElement(context, name);
}
if (isinf(value)) {
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
return value > 0 ? addJSONData(context, "1e999", 5) : addJSONData(context, "-1e999", 6);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: are these the expected values at Sentry or just replicating KSCrash?

}

char buff[50];
int written = snprintf(buff, sizeof(buff), "%lg", value);
if (written < 0) {
return SentryCrashJSON_ERROR_INVALID_CHARACTER;
} else if (written >= (int)sizeof(buff)) {
return SentryCrashJSON_ERROR_DATA_TOO_LONG;
}
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
char buff[50];
snprintf(buff, sizeof(buff), "%lg", value);
return addJSONData(context, buff, (int)strlen(buff));
return addJSONData(context, buff, written);
}

int
sentrycrashjson_addIntegerElement(
SentryCrashJSONEncodeContext *const context, const char *const name, int64_t value)
{
char buff[30];
int written = snprintf(buff, sizeof(buff), "%" PRId64, value);
if (written < 0) {
return SentryCrashJSON_ERROR_INVALID_CHARACTER;
} else if (written >= (int)sizeof(buff)) {
return SentryCrashJSON_ERROR_DATA_TOO_LONG;
}
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
char buff[30];
snprintf(buff, sizeof(buff), "%" PRId64, value);
return addJSONData(context, buff, (int)strlen(buff));
return addJSONData(context, buff, written);
}

int
sentrycrashjson_addUIntegerElement(
SentryCrashJSONEncodeContext *const context, const char *const name, uint64_t value)
{
char buff[30];
int written = snprintf(buff, sizeof(buff), "%" PRIu64, value);
if (written < 0) {
return SentryCrashJSON_ERROR_INVALID_CHARACTER;
} else if (written >= (int)sizeof(buff)) {
return SentryCrashJSON_ERROR_DATA_TOO_LONG;
}
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
char buff[30];
snprintf(buff, sizeof(buff), "%" PRIu64, value);
return addJSONData(context, buff, (int)strlen(buff));
return addJSONData(context, buff, written);
}

int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,14 @@ final class SentrySystemEventBreadcrumbs: NSObject {
let currentLevel = currentDevice.batteryLevel
var batteryData: [String: Any] = [:]

// W3C spec says level must be null if it is unknown
if currentState != .unknown && currentLevel != -1.0 {
// W3C spec says level must be null if it is unknown.
// Also guard against non-finite or out-of-range values so they never
// reach the crash-scope JSON encoder as invalid floats.
if currentState != .unknown && currentLevel.isFinite && (0...1).contains(currentLevel) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: Does apple even report non finite values?

let w3cLevel = currentLevel * 100
batteryData["level"] = NSNumber(value: w3cLevel)
} else {
SentrySDKLog.debug("batteryLevel is unknown.")
SentrySDKLog.debug("batteryLevel is unknown or has unexpected value: \(currentLevel)")
}

batteryData["plugged"] = isPlugged
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,59 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase {
assertBatteryBreadcrumb(charging: false, level: 100)
}

func testBatteryInfinityLevelOmitsLevel() {
let currentDevice = MyUIDevice(batteryLevel: .infinity, batteryState: .charging)

sut = fixture.getSut(currentDevice: currentDevice)

postBatteryLevelNotification(uiDevice: currentDevice)

XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count)
if let data = fixture.delegate.addCrumbInvocations.first?.data {
XCTAssertNil(data["level"], "Infinity battery level should be omitted")
XCTAssertEqual("BATTERY_STATE_CHANGE", data["action"] as? String)
}
}

func testBatteryNegativeInfinityLevelOmitsLevel() {
let currentDevice = MyUIDevice(batteryLevel: -.infinity, batteryState: .charging)

sut = fixture.getSut(currentDevice: currentDevice)

postBatteryLevelNotification(uiDevice: currentDevice)

XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count)
if let data = fixture.delegate.addCrumbInvocations.first?.data {
XCTAssertNil(data["level"], "Negative infinity battery level should be omitted")
}
}

func testBatteryNaNLevelOmitsLevel() {
let currentDevice = MyUIDevice(batteryLevel: .nan, batteryState: .charging)

sut = fixture.getSut(currentDevice: currentDevice)

postBatteryLevelNotification(uiDevice: currentDevice)

XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count)
if let data = fixture.delegate.addCrumbInvocations.first?.data {
XCTAssertNil(data["level"], "NaN battery level should be omitted")
}
}

func testBatteryOutOfRangeLevelOmitsLevel() {
let currentDevice = MyUIDevice(batteryLevel: 1.5, batteryState: .charging)

sut = fixture.getSut(currentDevice: currentDevice)

postBatteryLevelNotification(uiDevice: currentDevice)

XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count)
if let data = fixture.delegate.addCrumbInvocations.first?.data {
XCTAssertNil(data["level"], "Out-of-range battery level should be omitted")
}
}

func testBatteryUIDeviceNilNotification() {
let currentDevice = MyUIDevice()

Expand Down
78 changes: 78 additions & 0 deletions Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,84 @@ - (void)testSerializeDeserializeNANDouble
XCTAssertTrue([[result objectAtIndex:0] isKindOfClass:[NSNull class]]);
}

- (void)testSerializeDeserializePositiveInfinityFloat
{
NSError *error = (NSError *)self;
NSString *expected = @"[1e999]";
float infValue = INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithFloat:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
}

- (void)testSerializeDeserializeNegativeInfinityFloat
{
NSError *error = (NSError *)self;
NSString *expected = @"[-1e999]";
float infValue = -INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithFloat:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
XCTAssertTrue([[result objectAtIndex:0] doubleValue] < 0, @"Should be negative infinity");
}

- (void)testSerializeDeserializePositiveInfinityDouble
{
NSError *error = (NSError *)self;
NSString *expected = @"[1e999]";
double infValue = (double)INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithDouble:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
}

- (void)testSerializeDeserializeNegativeInfinityDouble
{
NSError *error = (NSError *)self;
NSString *expected = @"[-1e999]";
double infValue = -(double)INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithDouble:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
XCTAssertTrue([[result objectAtIndex:0] doubleValue] < 0, @"Should be negative infinity");
}

- (void)testSerializeDeserializeChar
{
NSError *error = (NSError *)self;
Expand Down
Loading