/google_toolbox/Foundation/GTMBase64Test.m

https://github.com/chuckprice/objectiveresource · Objective C · 437 lines · 335 code · 50 blank · 52 comment · 28 complexity · b464d42c80224b1dd647ce1790b71a3e MD5 · raw file

  1. //
  2. // GTMBase64Test.m
  3. //
  4. // Copyright 2006-2008 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. #import "GTMSenTestCase.h"
  19. #import "GTMBase64.h"
  20. #include <stdlib.h> // for randiom/srandomdev
  21. static void FillWithRandom(char *data, NSUInteger len) {
  22. char *max = data + len;
  23. for ( ; data < max ; ++data) {
  24. *data = random() & 0xFF;
  25. }
  26. }
  27. static BOOL NoEqualChar(NSData *data) {
  28. const char *scan = [data bytes];
  29. const char *max = scan + [data length];
  30. for ( ; scan < max ; ++scan) {
  31. if (*scan == '=') {
  32. return NO;
  33. }
  34. }
  35. return YES;
  36. }
  37. @interface GTMBase64Test : GTMTestCase
  38. @end
  39. @implementation GTMBase64Test
  40. - (void)setUp {
  41. // seed random from /dev/random
  42. srandomdev();
  43. }
  44. - (void)testBase64 {
  45. // generate a range of sizes w/ random content
  46. for (int x = 1 ; x < 1024 ; ++x) {
  47. NSMutableData *data = [NSMutableData data];
  48. STAssertNotNil(data, @"failed to alloc data block");
  49. [data setLength:x];
  50. FillWithRandom([data mutableBytes], [data length]);
  51. // w/ *Bytes apis
  52. NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]];
  53. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  54. @"encoded size via *Bytes apis should be a multiple of 4");
  55. NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes]
  56. length:[encoded length]];
  57. STAssertEqualObjects(data, dataPrime,
  58. @"failed to round trip via *Bytes apis");
  59. // w/ *Data apis
  60. encoded = [GTMBase64 encodeData:data];
  61. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  62. @"encoded size via *Data apis should be a multiple of 4");
  63. dataPrime = [GTMBase64 decodeData:encoded];
  64. STAssertEqualObjects(data, dataPrime,
  65. @"failed to round trip via *Data apis");
  66. // Bytes to String and back
  67. NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes]
  68. length:[data length]];
  69. STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
  70. @"encoded size for Bytes to Strings should be a multiple of 4");
  71. dataPrime = [GTMBase64 decodeString:encodedString];
  72. STAssertEqualObjects(data, dataPrime,
  73. @"failed to round trip for Bytes to Strings");
  74. // Data to String and back
  75. encodedString = [GTMBase64 stringByEncodingData:data];
  76. STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
  77. @"encoded size for Data to Strings should be a multiple of 4");
  78. dataPrime = [GTMBase64 decodeString:encodedString];
  79. STAssertEqualObjects(data, dataPrime,
  80. @"failed to round trip for Bytes to Strings");
  81. }
  82. {
  83. // now test all byte values
  84. NSMutableData *data = [NSMutableData data];
  85. STAssertNotNil(data, @"failed to alloc data block");
  86. [data setLength:256];
  87. unsigned char *scan = (unsigned char*)[data mutableBytes];
  88. for (int x = 0 ; x <= 255 ; ++x) {
  89. *scan++ = x;
  90. }
  91. // w/ *Bytes apis
  92. NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]];
  93. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  94. @"encoded size via *Bytes apis should be a multiple of 4");
  95. NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes]
  96. length:[encoded length]];
  97. STAssertEqualObjects(data, dataPrime,
  98. @"failed to round trip via *Bytes apis");
  99. // w/ *Data apis
  100. encoded = [GTMBase64 encodeData:data];
  101. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  102. @"encoded size via *Data apis should be a multiple of 4");
  103. dataPrime = [GTMBase64 decodeData:encoded];
  104. STAssertEqualObjects(data, dataPrime,
  105. @"failed to round trip via *Data apis");
  106. // Bytes to String and back
  107. NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes]
  108. length:[data length]];
  109. STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
  110. @"encoded size for Bytes to Strings should be a multiple of 4");
  111. dataPrime = [GTMBase64 decodeString:encodedString];
  112. STAssertEqualObjects(data, dataPrime,
  113. @"failed to round trip for Bytes to Strings");
  114. // Data to String and back
  115. encodedString = [GTMBase64 stringByEncodingData:data];
  116. STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
  117. @"encoded size for Data to Strings should be a multiple of 4");
  118. dataPrime = [GTMBase64 decodeString:encodedString];
  119. STAssertEqualObjects(data, dataPrime,
  120. @"failed to round trip for Data to Strings");
  121. }
  122. {
  123. // test w/ a mix of spacing characters
  124. // generate some data, encode it, and add spaces
  125. NSMutableData *data = [NSMutableData data];
  126. STAssertNotNil(data, @"failed to alloc data block");
  127. [data setLength:253]; // should get some padding chars on the end
  128. FillWithRandom([data mutableBytes], [data length]);
  129. NSString *encodedString = [GTMBase64 stringByEncodingData:data];
  130. NSMutableString *encodedAndSpaced =
  131. [[encodedString mutableCopy] autorelease];
  132. NSString *spaces[] = { @"\t", @"\n", @"\r", @" " };
  133. const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*);
  134. for (int x = 0 ; x < 512 ; ++x) {
  135. NSUInteger offset = random() % ([encodedAndSpaced length] + 1);
  136. [encodedAndSpaced insertString:spaces[random() % numSpaces]
  137. atIndex:offset];
  138. }
  139. // we'll need it as data for apis
  140. NSData *encodedAsData =
  141. [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding];
  142. STAssertNotNil(encodedAsData, @"failed to extract from string");
  143. STAssertEquals([encodedAsData length], [encodedAndSpaced length],
  144. @"lengths for encoded string and data didn't match?");
  145. // all the decode modes
  146. NSData *dataPrime = [GTMBase64 decodeData:encodedAsData];
  147. STAssertEqualObjects(data, dataPrime,
  148. @"failed Data decode w/ spaces");
  149. dataPrime = [GTMBase64 decodeBytes:[encodedAsData bytes]
  150. length:[encodedAsData length]];
  151. STAssertEqualObjects(data, dataPrime,
  152. @"failed Bytes decode w/ spaces");
  153. dataPrime = [GTMBase64 decodeString:encodedAndSpaced];
  154. STAssertEqualObjects(data, dataPrime,
  155. @"failed String decode w/ spaces");
  156. }
  157. }
  158. - (void)testWebSafeBase64 {
  159. // loop to test w/ and w/o padding
  160. for (int paddedLoop = 0; paddedLoop < 2 ; ++paddedLoop) {
  161. BOOL padded = (paddedLoop == 1);
  162. // generate a range of sizes w/ random content
  163. for (int x = 1 ; x < 1024 ; ++x) {
  164. NSMutableData *data = [NSMutableData data];
  165. STAssertNotNil(data, @"failed to alloc data block");
  166. [data setLength:x];
  167. FillWithRandom([data mutableBytes], [data length]);
  168. // w/ *Bytes apis
  169. NSData *encoded = [GTMBase64 webSafeEncodeBytes:[data bytes]
  170. length:[data length]
  171. padded:padded];
  172. if (padded) {
  173. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  174. @"encoded size via *Bytes apis should be a multiple of 4");
  175. } else {
  176. STAssertTrue(NoEqualChar(encoded),
  177. @"encoded via *Bytes apis had a base64 padding char");
  178. }
  179. NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes]
  180. length:[encoded length]];
  181. STAssertEqualObjects(data, dataPrime,
  182. @"failed to round trip via *Bytes apis");
  183. // w/ *Data apis
  184. encoded = [GTMBase64 webSafeEncodeData:data padded:padded];
  185. if (padded) {
  186. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  187. @"encoded size via *Data apis should be a multiple of 4");
  188. } else {
  189. STAssertTrue(NoEqualChar(encoded),
  190. @"encoded via *Data apis had a base64 padding char");
  191. }
  192. dataPrime = [GTMBase64 webSafeDecodeData:encoded];
  193. STAssertEqualObjects(data, dataPrime,
  194. @"failed to round trip via *Data apis");
  195. // Bytes to String and back
  196. NSString *encodedString =
  197. [GTMBase64 stringByWebSafeEncodingBytes:[data bytes]
  198. length:[data length]
  199. padded:padded];
  200. if (padded) {
  201. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  202. @"encoded size via *Bytes apis should be a multiple of 4");
  203. } else {
  204. STAssertTrue(NoEqualChar(encoded),
  205. @"encoded via Bytes to Strings had a base64 padding char");
  206. }
  207. dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
  208. STAssertEqualObjects(data, dataPrime,
  209. @"failed to round trip for Bytes to Strings");
  210. // Data to String and back
  211. encodedString =
  212. [GTMBase64 stringByWebSafeEncodingData:data padded:padded];
  213. if (padded) {
  214. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  215. @"encoded size via *Data apis should be a multiple of 4");
  216. } else {
  217. STAssertTrue(NoEqualChar(encoded),
  218. @"encoded via Data to Strings had a base64 padding char");
  219. }
  220. dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
  221. STAssertEqualObjects(data, dataPrime,
  222. @"failed to round trip for Data to Strings");
  223. }
  224. {
  225. // now test all byte values
  226. NSMutableData *data = [NSMutableData data];
  227. STAssertNotNil(data, @"failed to alloc data block");
  228. [data setLength:256];
  229. unsigned char *scan = (unsigned char*)[data mutableBytes];
  230. for (int x = 0 ; x <= 255 ; ++x) {
  231. *scan++ = x;
  232. }
  233. // w/ *Bytes apis
  234. NSData *encoded =
  235. [GTMBase64 webSafeEncodeBytes:[data bytes]
  236. length:[data length]
  237. padded:padded];
  238. if (padded) {
  239. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  240. @"encoded size via *Bytes apis should be a multiple of 4");
  241. } else {
  242. STAssertTrue(NoEqualChar(encoded),
  243. @"encoded via *Bytes apis had a base64 padding char");
  244. }
  245. NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes]
  246. length:[encoded length]];
  247. STAssertEqualObjects(data, dataPrime,
  248. @"failed to round trip via *Bytes apis");
  249. // w/ *Data apis
  250. encoded = [GTMBase64 webSafeEncodeData:data padded:padded];
  251. if (padded) {
  252. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  253. @"encoded size via *Data apis should be a multiple of 4");
  254. } else {
  255. STAssertTrue(NoEqualChar(encoded),
  256. @"encoded via *Data apis had a base64 padding char");
  257. }
  258. dataPrime = [GTMBase64 webSafeDecodeData:encoded];
  259. STAssertEqualObjects(data, dataPrime,
  260. @"failed to round trip via *Data apis");
  261. // Bytes to String and back
  262. NSString *encodedString =
  263. [GTMBase64 stringByWebSafeEncodingBytes:[data bytes]
  264. length:[data length]
  265. padded:padded];
  266. if (padded) {
  267. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  268. @"encoded size via *Bytes apis should be a multiple of 4");
  269. } else {
  270. STAssertTrue(NoEqualChar(encoded),
  271. @"encoded via Bytes to Strings had a base64 padding char");
  272. }
  273. dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
  274. STAssertEqualObjects(data, dataPrime,
  275. @"failed to round trip for Bytes to Strings");
  276. // Data to String and back
  277. encodedString =
  278. [GTMBase64 stringByWebSafeEncodingData:data padded:padded];
  279. if (padded) {
  280. STAssertEquals(([encoded length] % 4), (NSUInteger)0,
  281. @"encoded size via *Data apis should be a multiple of 4");
  282. } else {
  283. STAssertTrue(NoEqualChar(encoded),
  284. @"encoded via Data to Strings had a base64 padding char");
  285. }
  286. dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
  287. STAssertEqualObjects(data, dataPrime,
  288. @"failed to round trip for Data to Strings");
  289. }
  290. {
  291. // test w/ a mix of spacing characters
  292. // generate some data, encode it, and add spaces
  293. NSMutableData *data = [NSMutableData data];
  294. STAssertNotNil(data, @"failed to alloc data block");
  295. [data setLength:253]; // should get some padding chars on the end
  296. FillWithRandom([data mutableBytes], [data length]);
  297. NSString *encodedString = [GTMBase64 stringByWebSafeEncodingData:data
  298. padded:padded];
  299. NSMutableString *encodedAndSpaced =
  300. [[encodedString mutableCopy] autorelease];
  301. NSString *spaces[] = { @"\t", @"\n", @"\r", @" " };
  302. const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*);
  303. for (int x = 0 ; x < 512 ; ++x) {
  304. NSUInteger offset = random() % ([encodedAndSpaced length] + 1);
  305. [encodedAndSpaced insertString:spaces[random() % numSpaces]
  306. atIndex:offset];
  307. }
  308. // we'll need it as data for apis
  309. NSData *encodedAsData =
  310. [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding];
  311. STAssertNotNil(encodedAsData, @"failed to extract from string");
  312. STAssertEquals([encodedAsData length], [encodedAndSpaced length],
  313. @"lengths for encoded string and data didn't match?");
  314. // all the decode modes
  315. NSData *dataPrime = [GTMBase64 webSafeDecodeData:encodedAsData];
  316. STAssertEqualObjects(data, dataPrime,
  317. @"failed Data decode w/ spaces");
  318. dataPrime = [GTMBase64 webSafeDecodeBytes:[encodedAsData bytes]
  319. length:[encodedAsData length]];
  320. STAssertEqualObjects(data, dataPrime,
  321. @"failed Bytes decode w/ spaces");
  322. dataPrime = [GTMBase64 webSafeDecodeString:encodedAndSpaced];
  323. STAssertEqualObjects(data, dataPrime,
  324. @"failed String decode w/ spaces");
  325. }
  326. } // paddedLoop
  327. }
  328. - (void)testErrors {
  329. const int something = 0;
  330. NSString *nonAscString = [NSString stringWithUTF8String:"This test ©™®๒०᠐٧"];
  331. STAssertNil([GTMBase64 encodeData:nil], @"it worked?");
  332. STAssertNil([GTMBase64 decodeData:nil], @"it worked?");
  333. STAssertNil([GTMBase64 encodeBytes:NULL length:10], @"it worked?");
  334. STAssertNil([GTMBase64 encodeBytes:&something length:0], @"it worked?");
  335. STAssertNil([GTMBase64 decodeBytes:NULL length:10], @"it worked?");
  336. STAssertNil([GTMBase64 decodeBytes:&something length:0], @"it worked?");
  337. STAssertNil([GTMBase64 stringByEncodingData:nil], @"it worked?");
  338. STAssertNil([GTMBase64 stringByEncodingBytes:NULL length:10], @"it worked?");
  339. STAssertNil([GTMBase64 stringByEncodingBytes:&something length:0], @"it worked?");
  340. STAssertNil([GTMBase64 decodeString:nil], @"it worked?");
  341. // test some pads at the end that aren't right
  342. STAssertNil([GTMBase64 decodeString:@"=="], @"it worked?"); // just pads
  343. STAssertNil([GTMBase64 decodeString:@"vw="], @"it worked?"); // missing pad (in state 2)
  344. STAssertNil([GTMBase64 decodeString:@"vw"], @"it worked?"); // missing pad (in state 2)
  345. STAssertNil([GTMBase64 decodeString:@"NNw"], @"it worked?"); // missing pad (in state 3)
  346. STAssertNil([GTMBase64 decodeString:@"vw=v"], @"it worked?"); // missing pad, has something else
  347. STAssertNil([GTMBase64 decodeString:@"v="], @"it worked?"); // missing a needed char, has pad instead
  348. STAssertNil([GTMBase64 decodeString:@"v"], @"it worked?"); // missing a needed char
  349. STAssertNil([GTMBase64 decodeString:@"vw== vw"], @"it worked?");
  350. STAssertNil([GTMBase64 decodeString:nonAscString], @"it worked?");
  351. STAssertNil([GTMBase64 decodeString:@"@@@not valid###"], @"it worked?");
  352. // carefully crafted bad input to make sure we don't overwalk
  353. STAssertNil([GTMBase64 decodeString:@"WD=="], @"it worked?");
  354. STAssertNil([GTMBase64 webSafeEncodeData:nil padded:YES], @"it worked?");
  355. STAssertNil([GTMBase64 webSafeDecodeData:nil], @"it worked?");
  356. STAssertNil([GTMBase64 webSafeEncodeBytes:NULL length:10 padded:YES],
  357. @"it worked?");
  358. STAssertNil([GTMBase64 webSafeEncodeBytes:&something length:0 padded:YES],
  359. @"it worked?");
  360. STAssertNil([GTMBase64 webSafeDecodeBytes:NULL length:10], @"it worked?");
  361. STAssertNil([GTMBase64 webSafeDecodeBytes:&something length:0], @"it worked?");
  362. STAssertNil([GTMBase64 stringByWebSafeEncodingData:nil padded:YES],
  363. @"it worked?");
  364. STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:NULL
  365. length:10
  366. padded:YES],
  367. @"it worked?");
  368. STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:&something
  369. length:0
  370. padded:YES],
  371. @"it worked?");
  372. STAssertNil([GTMBase64 webSafeDecodeString:nil], @"it worked?");
  373. // test some pads at the end that aren't right
  374. STAssertNil([GTMBase64 webSafeDecodeString:@"=="], @"it worked?"); // just pad chars
  375. STAssertNil([GTMBase64 webSafeDecodeString:@"aw="], @"it worked?"); // missing pad
  376. STAssertNil([GTMBase64 webSafeDecodeString:@"aw=a"], @"it worked?"); // missing pad, has something else
  377. STAssertNil([GTMBase64 webSafeDecodeString:@"a"], @"it worked?"); // missing a needed char
  378. STAssertNil([GTMBase64 webSafeDecodeString:@"a="], @"it worked?"); // missing a needed char, has pad instead
  379. STAssertNil([GTMBase64 webSafeDecodeString:@"aw== a"], @"it worked?"); // missing pad
  380. STAssertNil([GTMBase64 webSafeDecodeString:nonAscString], @"it worked?");
  381. STAssertNil([GTMBase64 webSafeDecodeString:@"@@@not valid###"], @"it worked?");
  382. // carefully crafted bad input to make sure we don't overwalk
  383. STAssertNil([GTMBase64 webSafeDecodeString:@"WD=="], @"it worked?");
  384. // make sure our local helper is working right
  385. STAssertFalse(NoEqualChar([NSData dataWithBytes:"aa=zz" length:5]), @"");
  386. }
  387. @end