OctagonTrustTests+Errors.m   [plain text]


/*
 * Copyright (c) 2020 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#import <Foundation/Foundation.h>
#import "OctagonTrustTests.h"

#if OCTAGON

NS_ASSUME_NONNULL_BEGIN


@implementation OctagonTrustTests (OctagonTrustTestsErrors)
- (void) testDepthCountOnErrors
{
    SecErrorSetOverrideNestedErrorCappingIsEnabled(true);
    //previousError == nil case
    CFErrorRef errorWithoutPreviousSet = NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, &errorWithoutPreviousSet, NULL, CFSTR("no previous error"));
    XCTAssertNotNil((__bridge NSError*) errorWithoutPreviousSet, "errorWithoutPreviousSet should not be nil");
    NSDictionary *userInfo = [(__bridge NSError*)errorWithoutPreviousSet userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    NSNumber *depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 0, "depth should be 0");

    //previousError is set with a depth of 0
    CFErrorRef newError = NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, errorWithoutPreviousSet, &newError, NULL, CFSTR("previousError exists!"));
    XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
    userInfo = [(__bridge NSError*)newError userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 1, "depth should be 1");

    //previousError is set with a dpeth of 1
    CFErrorRef secondError = NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, newError, &secondError, NULL, CFSTR("using previous error that already has a chain"));
    XCTAssertNotNil((__bridge NSError*) secondError, "secondError should not be nil");
    userInfo = [(__bridge NSError*)secondError userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 2, "depth should be 2");
}
- (void) testErrorCap
{
    SecErrorSetOverrideNestedErrorCappingIsEnabled(true);
    //previousError == nil case
    CFErrorRef previous= NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, &previous, NULL, CFSTR("no previous error"));
    XCTAssertNotNil((__bridge NSError*) previous, "errorWithoutPreviousSet should not be nil");
    NSDictionary *userInfo = [(__bridge NSError*)previous userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    NSNumber *depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 0, "depth should be 0");

    //stay within the cap limit
    CFErrorRef newError = NULL;
    for(int i = 0; i<200; i++) {
        SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("previousError exists %d!"), i);
        XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
        userInfo = [(__bridge NSError*)newError userInfo];
        XCTAssertNotNil(userInfo, "userInfo should not be nil");
        depth = userInfo[@"numberOfErrorsDeep"];
        XCTAssertNotNil(depth, "depth should not be nil");
        XCTAssertEqual([depth longValue], i+1, "depth should be i+1");
        previous = newError;
        newError = nil;
    }

    //now blow the cap limit
    previous = NULL;
    newError = NULL;
    int depthCounter = 0;

    for(int i = 0; i < 500; i++) {
        SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("previousError exists %d!"), i);
        XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
        userInfo = [(__bridge NSError*)newError userInfo];
        XCTAssertNotNil(userInfo, "userInfo should not be nil");
        depth = userInfo[@"numberOfErrorsDeep"];
        if (i >= 201) {
            NSDictionary* previousUserInfo = [(__bridge NSError*)previous userInfo];
            NSDictionary* newErrorUserInfo = [(__bridge NSError*)newError userInfo];
            XCTAssertTrue([previousUserInfo isEqualToDictionary:newErrorUserInfo], "newError should equal previous error");
        }

        if (i <= 199) {
            XCTAssertNotNil(depth, "depth should not be nil");
            XCTAssertEqual([depth longValue], depthCounter, "depth should be %d", depthCounter);
            depthCounter++;
        } else {
            userInfo = [(__bridge NSError*)newError userInfo];
            depth = userInfo[@"numberOfErrorsDeep"];
            XCTAssertNotNil(depth, "depth should not be nil");
            XCTAssertEqual([depth longValue], 200, "depth should be 200");
        }
        
        previous = newError;
        newError = nil;
    }
}

- (void) testErrorCapWithFeatureFlagDisable
{
    SecErrorSetOverrideNestedErrorCappingIsEnabled(false);
    //previousError == nil case
    CFErrorRef previous= NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, &previous, NULL, CFSTR("no previous error"));
    XCTAssertNotNil((__bridge NSError*) previous, "errorWithoutPreviousSet should not be nil");
    NSDictionary *userInfo = [(__bridge NSError*)previous userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    NSNumber *depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 0, "depth should be 0");

    //stay within the cap limit
    CFErrorRef newError = NULL;
    for(int i = 0; i<200; i++) {
        SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("previousError exists %d!"), i);
        XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
        userInfo = [(__bridge NSError*)newError userInfo];
        XCTAssertNotNil(userInfo, "userInfo should not be nil");
        depth = userInfo[@"numberOfErrorsDeep"];
        XCTAssertNotNil(depth, "depth should not be nil");
        XCTAssertEqual([depth longValue], i+1, "depth should be i+1");
        previous = newError;
        newError = nil;
    }

    //now blow the cap limit
    previous = NULL;
    newError = NULL;

    for(int i = 0; i < 500; i++) {
        SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("previousError exists %d!"), i);
        XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
        userInfo = [(__bridge NSError*)newError userInfo];
        XCTAssertNotNil(userInfo, "userInfo should not be nil");
        depth = userInfo[@"numberOfErrorsDeep"];
        XCTAssertNotNil(depth, "depth should not be nil");
        XCTAssertEqual([depth longValue], i, "depth should be %d", i);
        previous = newError;
        newError = nil;
    }
}

- (void) testNestedErrorCreationUsingSameErrorParameters
{
    SecErrorSetOverrideNestedErrorCappingIsEnabled(true);
    //previousError == nil case
    CFErrorRef previous= NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, &previous, NULL, CFSTR("no previous error"));
    XCTAssertNotNil((__bridge NSError*) previous, "previous should not be nil");
    NSDictionary *userInfo = [(__bridge NSError*)previous userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    NSNumber *depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 0, "depth should be 0");

    //now pass the exact same error
    CFErrorRef newError = NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("no previous error"));
    XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
    userInfo = [(__bridge NSError*)newError userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 0, "depth should be 0");

    previous = newError;
    newError = NULL;
    SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("no previous error1"));
    XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
    userInfo = [(__bridge NSError*)newError userInfo];
    XCTAssertNotNil(userInfo, "userInfo should not be nil");
    depth = userInfo[@"numberOfErrorsDeep"];
    XCTAssertNotNil(depth, "depth should not be nil");
    XCTAssertEqual([depth longValue], 1, "depth should be 1");

    previous = newError;
    newError = NULL;
    for(int i = 0; i < 500; i++) {
        SOSCreateErrorWithFormat(kSOSErrorNoCircle, previous, &newError, NULL, CFSTR("no previous error1"));
        XCTAssertNotNil((__bridge NSError*) newError, "newError should not be nil");
        userInfo = [(__bridge NSError*)newError userInfo];
        XCTAssertNotNil(userInfo, "userInfo should not be nil");
        depth = userInfo[@"numberOfErrorsDeep"];
        XCTAssertNotNil(depth, "depth should not be nil");
        XCTAssertEqual([depth longValue], 1, "depth should be 1");
        previous = newError;
        newError = nil;
    }
}

@end
NS_ASSUME_NONNULL_END

#endif