diff options
Diffstat (limited to 'Foundation/GTMBase64Test.m')
-rw-r--r-- | Foundation/GTMBase64Test.m | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/Foundation/GTMBase64Test.m b/Foundation/GTMBase64Test.m new file mode 100644 index 0000000..358c6ec --- /dev/null +++ b/Foundation/GTMBase64Test.m @@ -0,0 +1,437 @@ +// +// GTMBase64Test.m +// +// Copyright 2006-2008 Google Inc. +// +// 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. +// + +#import "GTMSenTestCase.h" +#import "GTMBase64.h" +#include <stdlib.h> // for randiom/srandomdev + +static void FillWithRandom(char *data, NSUInteger len) { + char *max = data + len; + for ( ; data < max ; ++data) { + *data = random() & 0xFF; + } +} + +static BOOL NoEqualChar(NSData *data) { + const char *scan = [data bytes]; + const char *max = scan + [data length]; + for ( ; scan < max ; ++scan) { + if (*scan == '=') { + return NO; + } + } + return YES; +} + +@interface GTMBase64Test : SenTestCase +@end + +@implementation GTMBase64Test + +- (void)setUp { + // seed random from /dev/random + srandomdev(); +} + +- (void)testBase64 { + // generate a range of sizes w/ random content + for (int x = 1 ; x < 1024 ; ++x) { + NSMutableData *data = [NSMutableData data]; + STAssertNotNil(data, @"failed to alloc data block"); + + [data setLength:x]; + FillWithRandom([data mutableBytes], [data length]); + + // w/ *Bytes apis + NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]]; + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Bytes apis should be a multiple of 4"); + NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes] + length:[encoded length]]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Bytes apis"); + + // w/ *Data apis + encoded = [GTMBase64 encodeData:data]; + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Data apis should be a multiple of 4"); + dataPrime = [GTMBase64 decodeData:encoded]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Data apis"); + + // Bytes to String and back + NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes] + length:[data length]]; + STAssertEquals(([encodedString length] % 4), (NSUInteger)0, + @"encoded size for Bytes to Strings should be a multiple of 4"); + dataPrime = [GTMBase64 decodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Bytes to Strings"); + + // Data to String and back + encodedString = [GTMBase64 stringByEncodingData:data]; + STAssertEquals(([encodedString length] % 4), (NSUInteger)0, + @"encoded size for Data to Strings should be a multiple of 4"); + dataPrime = [GTMBase64 decodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Bytes to Strings"); + } + + { + // now test all byte values + NSMutableData *data = [NSMutableData data]; + STAssertNotNil(data, @"failed to alloc data block"); + + [data setLength:256]; + unsigned char *scan = (unsigned char*)[data mutableBytes]; + for (int x = 0 ; x <= 255 ; ++x) { + *scan++ = x; + } + + // w/ *Bytes apis + NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]]; + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Bytes apis should be a multiple of 4"); + NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes] + length:[encoded length]]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Bytes apis"); + + // w/ *Data apis + encoded = [GTMBase64 encodeData:data]; + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Data apis should be a multiple of 4"); + dataPrime = [GTMBase64 decodeData:encoded]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Data apis"); + + // Bytes to String and back + NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes] + length:[data length]]; + STAssertEquals(([encodedString length] % 4), (NSUInteger)0, + @"encoded size for Bytes to Strings should be a multiple of 4"); + dataPrime = [GTMBase64 decodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Bytes to Strings"); + + // Data to String and back + encodedString = [GTMBase64 stringByEncodingData:data]; + STAssertEquals(([encodedString length] % 4), (NSUInteger)0, + @"encoded size for Data to Strings should be a multiple of 4"); + dataPrime = [GTMBase64 decodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Data to Strings"); + } + + { + // test w/ a mix of spacing characters + + // generate some data, encode it, and add spaces + NSMutableData *data = [NSMutableData data]; + STAssertNotNil(data, @"failed to alloc data block"); + + [data setLength:253]; // should get some padding chars on the end + FillWithRandom([data mutableBytes], [data length]); + + NSString *encodedString = [GTMBase64 stringByEncodingData:data]; + NSMutableString *encodedAndSpaced = + [[encodedString mutableCopy] autorelease]; + + NSString *spaces[] = { @"\t", @"\n", @"\r", @" " }; + const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*); + for (int x = 0 ; x < 512 ; ++x) { + NSUInteger offset = random() % ([encodedAndSpaced length] + 1); + [encodedAndSpaced insertString:spaces[random() % numSpaces] + atIndex:offset]; + } + + // we'll need it as data for apis + NSData *encodedAsData = + [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding]; + STAssertNotNil(encodedAsData, @"failed to extract from string"); + STAssertEquals([encodedAsData length], [encodedAndSpaced length], + @"lengths for encoded string and data didn't match?"); + + // all the decode modes + NSData *dataPrime = [GTMBase64 decodeData:encodedAsData]; + STAssertEqualObjects(data, dataPrime, + @"failed Data decode w/ spaces"); + dataPrime = [GTMBase64 decodeBytes:[encodedAsData bytes] + length:[encodedAsData length]]; + STAssertEqualObjects(data, dataPrime, + @"failed Bytes decode w/ spaces"); + dataPrime = [GTMBase64 decodeString:encodedAndSpaced]; + STAssertEqualObjects(data, dataPrime, + @"failed String decode w/ spaces"); + } +} + +- (void)testWebSafeBase64 { + // loop to test w/ and w/o padding + for (int paddedLoop = 0; paddedLoop < 2 ; ++paddedLoop) { + BOOL padded = (paddedLoop == 1); + + // generate a range of sizes w/ random content + for (int x = 1 ; x < 1024 ; ++x) { + NSMutableData *data = [NSMutableData data]; + STAssertNotNil(data, @"failed to alloc data block"); + + [data setLength:x]; + FillWithRandom([data mutableBytes], [data length]); + + // w/ *Bytes apis + NSData *encoded = [GTMBase64 webSafeEncodeBytes:[data bytes] + length:[data length] + padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Bytes apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via *Bytes apis had a base64 padding char"); + } + NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes] + length:[encoded length]]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Bytes apis"); + + // w/ *Data apis + encoded = [GTMBase64 webSafeEncodeData:data padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Data apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via *Data apis had a base64 padding char"); + } + dataPrime = [GTMBase64 webSafeDecodeData:encoded]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Data apis"); + + // Bytes to String and back + NSString *encodedString = + [GTMBase64 stringByWebSafeEncodingBytes:[data bytes] + length:[data length] + padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Bytes apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via Bytes to Strings had a base64 padding char"); + } + dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Bytes to Strings"); + + // Data to String and back + encodedString = + [GTMBase64 stringByWebSafeEncodingData:data padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Data apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via Data to Strings had a base64 padding char"); + } + dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Data to Strings"); + } + + { + // now test all byte values + NSMutableData *data = [NSMutableData data]; + STAssertNotNil(data, @"failed to alloc data block"); + + [data setLength:256]; + unsigned char *scan = (unsigned char*)[data mutableBytes]; + for (int x = 0 ; x <= 255 ; ++x) { + *scan++ = x; + } + + // w/ *Bytes apis + NSData *encoded = + [GTMBase64 webSafeEncodeBytes:[data bytes] + length:[data length] + padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Bytes apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via *Bytes apis had a base64 padding char"); + } + NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes] + length:[encoded length]]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Bytes apis"); + + // w/ *Data apis + encoded = [GTMBase64 webSafeEncodeData:data padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Data apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via *Data apis had a base64 padding char"); + } + dataPrime = [GTMBase64 webSafeDecodeData:encoded]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Data apis"); + + // Bytes to String and back + NSString *encodedString = + [GTMBase64 stringByWebSafeEncodingBytes:[data bytes] + length:[data length] + padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Bytes apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via Bytes to Strings had a base64 padding char"); + } + dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Bytes to Strings"); + + // Data to String and back + encodedString = + [GTMBase64 stringByWebSafeEncodingData:data padded:padded]; + if (padded) { + STAssertEquals(([encoded length] % 4), (NSUInteger)0, + @"encoded size via *Data apis should be a multiple of 4"); + } else { + STAssertTrue(NoEqualChar(encoded), + @"encoded via Data to Strings had a base64 padding char"); + } + dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; + STAssertEqualObjects(data, dataPrime, + @"failed to round trip for Data to Strings"); + } + + { + // test w/ a mix of spacing characters + + // generate some data, encode it, and add spaces + NSMutableData *data = [NSMutableData data]; + STAssertNotNil(data, @"failed to alloc data block"); + + [data setLength:253]; // should get some padding chars on the end + FillWithRandom([data mutableBytes], [data length]); + + NSString *encodedString = [GTMBase64 stringByWebSafeEncodingData:data + padded:padded]; + NSMutableString *encodedAndSpaced = + [[encodedString mutableCopy] autorelease]; + + NSString *spaces[] = { @"\t", @"\n", @"\r", @" " }; + const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*); + for (int x = 0 ; x < 512 ; ++x) { + NSUInteger offset = random() % ([encodedAndSpaced length] + 1); + [encodedAndSpaced insertString:spaces[random() % numSpaces] + atIndex:offset]; + } + + // we'll need it as data for apis + NSData *encodedAsData = + [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding]; + STAssertNotNil(encodedAsData, @"failed to extract from string"); + STAssertEquals([encodedAsData length], [encodedAndSpaced length], + @"lengths for encoded string and data didn't match?"); + + // all the decode modes + NSData *dataPrime = [GTMBase64 webSafeDecodeData:encodedAsData]; + STAssertEqualObjects(data, dataPrime, + @"failed Data decode w/ spaces"); + dataPrime = [GTMBase64 webSafeDecodeBytes:[encodedAsData bytes] + length:[encodedAsData length]]; + STAssertEqualObjects(data, dataPrime, + @"failed Bytes decode w/ spaces"); + dataPrime = [GTMBase64 webSafeDecodeString:encodedAndSpaced]; + STAssertEqualObjects(data, dataPrime, + @"failed String decode w/ spaces"); + } + } // paddedLoop +} + +- (void)testErrors { + const int something = 0; + NSString *nonAscString = [NSString stringWithUTF8String:"This test ©™®๒०᠐٧"]; + + STAssertNil([GTMBase64 encodeData:nil], @"it worked?"); + STAssertNil([GTMBase64 decodeData:nil], @"it worked?"); + STAssertNil([GTMBase64 encodeBytes:NULL length:10], @"it worked?"); + STAssertNil([GTMBase64 encodeBytes:&something length:0], @"it worked?"); + STAssertNil([GTMBase64 decodeBytes:NULL length:10], @"it worked?"); + STAssertNil([GTMBase64 decodeBytes:&something length:0], @"it worked?"); + STAssertNil([GTMBase64 stringByEncodingData:nil], @"it worked?"); + STAssertNil([GTMBase64 stringByEncodingBytes:NULL length:10], @"it worked?"); + STAssertNil([GTMBase64 stringByEncodingBytes:&something length:0], @"it worked?"); + STAssertNil([GTMBase64 decodeString:nil], @"it worked?"); + // test some pads at the end that aren't right + STAssertNil([GTMBase64 decodeString:@"=="], @"it worked?"); // just pads + STAssertNil([GTMBase64 decodeString:@"vw="], @"it worked?"); // missing pad (in state 2) + STAssertNil([GTMBase64 decodeString:@"vw"], @"it worked?"); // missing pad (in state 2) + STAssertNil([GTMBase64 decodeString:@"NNw"], @"it worked?"); // missing pad (in state 3) + STAssertNil([GTMBase64 decodeString:@"vw=v"], @"it worked?"); // missing pad, has something else + STAssertNil([GTMBase64 decodeString:@"v="], @"it worked?"); // missing a needed char, has pad instead + STAssertNil([GTMBase64 decodeString:@"v"], @"it worked?"); // missing a needed char + STAssertNil([GTMBase64 decodeString:@"vw== vw"], @"it worked?"); + STAssertNil([GTMBase64 decodeString:nonAscString], @"it worked?"); + STAssertNil([GTMBase64 decodeString:@"@@@not valid###"], @"it worked?"); + // carefully crafted bad input to make sure we don't overwalk + STAssertNil([GTMBase64 decodeString:@"WD=="], @"it worked?"); + + STAssertNil([GTMBase64 webSafeEncodeData:nil padded:YES], @"it worked?"); + STAssertNil([GTMBase64 webSafeDecodeData:nil], @"it worked?"); + STAssertNil([GTMBase64 webSafeEncodeBytes:NULL length:10 padded:YES], + @"it worked?"); + STAssertNil([GTMBase64 webSafeEncodeBytes:&something length:0 padded:YES], + @"it worked?"); + STAssertNil([GTMBase64 webSafeDecodeBytes:NULL length:10], @"it worked?"); + STAssertNil([GTMBase64 webSafeDecodeBytes:&something length:0], @"it worked?"); + STAssertNil([GTMBase64 stringByWebSafeEncodingData:nil padded:YES], + @"it worked?"); + STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:NULL + length:10 + padded:YES], + @"it worked?"); + STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:&something + length:0 + padded:YES], + @"it worked?"); + STAssertNil([GTMBase64 webSafeDecodeString:nil], @"it worked?"); + // test some pads at the end that aren't right + STAssertNil([GTMBase64 webSafeDecodeString:@"=="], @"it worked?"); // just pad chars + STAssertNil([GTMBase64 webSafeDecodeString:@"aw="], @"it worked?"); // missing pad + STAssertNil([GTMBase64 webSafeDecodeString:@"aw=a"], @"it worked?"); // missing pad, has something else + STAssertNil([GTMBase64 webSafeDecodeString:@"a"], @"it worked?"); // missing a needed char + STAssertNil([GTMBase64 webSafeDecodeString:@"a="], @"it worked?"); // missing a needed char, has pad instead + STAssertNil([GTMBase64 webSafeDecodeString:@"aw== a"], @"it worked?"); // missing pad + STAssertNil([GTMBase64 webSafeDecodeString:nonAscString], @"it worked?"); + STAssertNil([GTMBase64 webSafeDecodeString:@"@@@not valid###"], @"it worked?"); + // carefully crafted bad input to make sure we don't overwalk + STAssertNil([GTMBase64 webSafeDecodeString:@"WD=="], @"it worked?"); + + // make sure our local helper is working right + STAssertFalse(NoEqualChar([NSData dataWithBytes:"aa=zz" length:5]), @""); +} + +@end |