PageRenderTime 320ms CodeModel.GetById 65ms app.highlight 242ms RepoModel.GetById 1ms app.codeStats 1ms

/Source/externals/GData/Source/Tests/GDataFeedTest.m

http://google-email-uploader-mac.googlecode.com/
Objective C | 2212 lines | 1440 code | 472 blank | 300 comment | 25 complexity | d08856f41ab760b3b32aadb99534327a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* Copyright (c) 2007 Google Inc.
   2 *
   3 * Licensed under the Apache License, Version 2.0 (the "License");
   4 * you may not use this file except in compliance with the License.
   5 * You may obtain a copy of the License at
   6 *
   7 *     http://www.apache.org/licenses/LICENSE-2.0
   8 *
   9 * Unless required by applicable law or agreed to in writing, software
  10 * distributed under the License is distributed on an "AS IS" BASIS,
  11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 * See the License for the specific language governing permissions and
  13 * limitations under the License.
  14 */
  15
  16//
  17//  GDataFeedTest.m
  18//
  19
  20#import "GData.h"
  21
  22#import "GDataFeedTest.h"
  23#import "GDataElementsTest.h"
  24
  25#import "GDataEntryCalendarEvent.h"
  26#import "GDataEntryYouTubeVideo.h"
  27#import "GDataMapConstants.h"
  28
  29
  30@implementation GDataFeedTest
  31
  32- (void)runTests:(TestKeyPathValues *)tests {
  33  
  34  // step through each feed test
  35  for (int testIndex = 0;
  36       tests[testIndex].str1 != nil;
  37       testIndex++) {
  38    
  39    // get the class of this feed and the path to the test file of xml data 
  40    //
  41    // The class name may specify a service version, like GDataFeedCalendar/2.0,
  42    // and may have a "-ignoreUnknown" suffix indicating unknown elements should be
  43    // ignored, like GDataFeedCalendar-ignore/2.0
  44
  45    NSString *className = tests[testIndex].str1;
  46    NSString *serviceVersion = nil;
  47    BOOL shouldIgnoreUnknowns = NO;
  48    
  49    NSArray *components = [className componentsSeparatedByString:@"/"];
  50    if ([components count] == 2) {
  51      className = [components objectAtIndex:0];
  52      serviceVersion = [components objectAtIndex:1];
  53    }
  54
  55    if ([className hasSuffix:@"-ignoreUnknown"]) {
  56      shouldIgnoreUnknowns = YES;
  57
  58      NSUInteger nameLen = [className length] - [@"-ignoreUnknown" length];
  59      className = [className substringToIndex:nameLen];
  60    }
  61    
  62#ifdef GDATA_TARGET_NAMESPACE
  63    className = [NSString stringWithFormat:@"%s_%@",
  64                 GDATA_TARGET_NAMESPACE_STRING, className];
  65#endif
  66
  67    Class gdataClass = NSClassFromString(className);
  68    STAssertNotNil(gdataClass, @"Cannot make class for class name: %@", className);
  69    
  70    NSString *feedPath = tests[testIndex].str2;
  71    NSData *data = [NSData dataWithContentsOfFile:feedPath];
  72    STAssertNotNil(data, @"Cannot read feed from %@", feedPath);
  73    
  74    // create the feed object
  75    GDataFeedBase *feed1 = nil;
  76    if ([gdataClass instancesRespondToSelector:@selector(initWithData:serviceVersion:)]) {
  77      feed1 = [[[gdataClass alloc] initWithData:data
  78                                 serviceVersion:serviceVersion
  79                           shouldIgnoreUnknowns:shouldIgnoreUnknowns] autorelease];
  80    } else {
  81      // this "feed" isn't a proper feed, and might be an entry
  82      NSError *error = nil;
  83      NSXMLDocument *doc = [[[NSXMLDocument alloc] initWithData:data
  84                                                        options:0
  85                                                          error:&error] autorelease];
  86      STAssertNotNil(doc, @"Cannot allocate XML document for %@, error %@",
  87                     feedPath, error);
  88
  89      NSXMLElement* root = [doc rootElement];
  90      feed1 = [[[gdataClass alloc] initWithXMLElement:root 
  91                                               parent:nil
  92                                       serviceVersion:serviceVersion
  93                                           surrogates:nil
  94                                 shouldIgnoreUnknowns:shouldIgnoreUnknowns] autorelease];
  95    }
  96    
  97    // copy the feed object 
  98    GDataObject *feed1copy = [[feed1 copy] autorelease];
  99    
 100    STAssertTrue([feed1 isEqual:feed1copy], @"Failed copy feed (%@) from %@ to %@",
 101                 feedPath, feed1, feed1copy);
 102
 103    // make a new feed object we'll test against from XML generated by the copy
 104    NSXMLElement *outputXML = [feed1copy XMLElement];
 105        
 106    GDataFeedBase *feed2 = [[[gdataClass alloc] initWithXMLElement:outputXML
 107                                                            parent:nil
 108                                                    serviceVersion:serviceVersion
 109                                                        surrogates:nil
 110                                              shouldIgnoreUnknowns:shouldIgnoreUnknowns] autorelease];
 111
 112    STAssertTrue([feed2 isEqual:feed1copy], @"Failed for %@ using XML \n  %@\n\nto convert\n  %@ \nto\n  %@",  
 113                 feedPath, outputXML, feed1copy, feed2);
 114
 115    // generate a description; this can fire an exception for an invalid keyPath
 116    // in the description record list
 117    STAssertNotNil([feed2 description], @"Could not generate description for %@",
 118                   feedPath);
 119    
 120    // step through all the key-value path tests
 121    while (1) {
 122      
 123      ++testIndex;
 124      
 125      NSString *keyPath = tests[testIndex].str1;
 126      NSString *expectedValue = tests[testIndex].str2;
 127      
 128      if (keyPath == nil || [keyPath length] == 0) break;
 129      
 130#if GDATA_USES_LIBXML
 131      // skip the XMLStrings until we can normalize whitespace and closing
 132      // brackets and other minor differences
 133      if ([keyPath hasSuffix:@".XMLString"]) continue;
 134#endif
 135
 136      NSString *result = [GDataElementsTest valueInObject:feed2
 137                                forKeyPathIncludingArrays:keyPath];
 138      
 139      // if the result wasn't a string but responds to stringValue, then
 140      // invoke that to get a string
 141      if ([expectedValue isKindOfClass:[NSString class]]
 142          && ![result isKindOfClass:[NSString class]]
 143          && [result respondsToSelector:@selector(stringValue)]) {
 144        
 145        result = [(id)result stringValue];     
 146      }
 147      
 148      // we'll test for equality unless the expected result begins "hasPrefix:"
 149      // or "contains:"
 150      if ([expectedValue hasPrefix:@"hasPrefix:"]) {
 151        NSString *prefix = [expectedValue substringFromIndex:[@"hasPrefix:" length]];
 152        STAssertTrue([result hasPrefix:prefix], @"failed object %@ \n testing key path '%@' for prefix:\n %@ \n!= prefix:\n %@", 
 153                     feed2, keyPath, result, prefix);  
 154        
 155      } else if ([expectedValue hasPrefix:@"contains:"]) {
 156        
 157        NSString *substring = [expectedValue substringFromIndex:[@"contains:" length]];
 158        NSRange range = [result rangeOfString:substring];
 159        STAssertTrue(result != nil && range.location != NSNotFound, 
 160                     @"failed object %@ \n testing key path '%@' for substring:\n %@ \n!= contains:\n %@", 
 161                     feed2, keyPath, result, substring);      
 162      } else {
 163
 164#ifdef GDATA_TARGET_NAMESPACE
 165        // tests for class name need the prefix added
 166        if ([keyPath hasSuffix:@"className"]
 167            && [expectedValue hasPrefix:@"GData"]) {
 168
 169          expectedValue = [NSString stringWithFormat:@"%s_%@",
 170                           GDATA_TARGET_NAMESPACE_STRING, expectedValue];
 171        }
 172#endif
 173
 174        STAssertTrue(AreEqualOrBothNil(result, expectedValue), @"failed object %@ \n testing key path '%@'\n %@ \n!= \n %@", 
 175                     feed2, keyPath, result, expectedValue);      
 176      }
 177    }
 178  }  
 179}
 180
 181- (void)testBooksFeed {
 182  
 183  //
 184  // Volumes Feed
 185  //
 186  TestKeyPathValues tests[] =
 187  { 
 188    { @"GDataFeedVolume", @"Tests/FeedBooksVolumesTest1.xml" },
 189    
 190    // GDataFeedVolume paths 
 191    { @"identifier", @"http://www.google.com/books/feeds/users/1728172424007912469/volumes" },
 192    { @"authors.0.name", @"1728172424007912469" },
 193    
 194    // GDataEntryVolume paths
 195    { @"entries.0.creators.0", @"Jim Davis" },
 196    { @"entries.0.dates.0", @"2006-01-31" },
 197    { @"entries.0.volumeDescriptions.0", @"contains:Pig Out" },
 198    { @"entries.0.embeddability", kGDataBooksNotEmbeddable },
 199    { @"entries.0.openAccess", kGDataBooksEnabled },
 200    { @"entries.0.formats.0", @"93 pages" },
 201    { @"entries.0.volumeIdentifiers.0", @"_PRJAAAACAAJ" },
 202    { @"entries.0.volumeIdentifiers.1", @"ISBN:0345464664" },
 203    { @"entries.0.publishers.0", @"Ballantine Books" },
 204    { @"entries.0.subjects.0", @"Humor" },
 205    { @"entries.0.volumeTitles.0", @"Garfield Pigs Out" },
 206    { @"entries.0.viewability", kGDataBooksViewNoPages },
 207    { @"entries.0.thumbnailLink.href", @"hasPrefix:http://bks2.books.google.com/books?id=_PRJAAAACAAJ" },
 208    { @"entries.0.previewLink.href", @"contains:id=_PRJAAAACAAJ&ie=ISO-8859-1" },
 209    { @"entries.0.infoLink.href", @"contains:id=_PRJAAAACAAJ&ie=ISO-8859-1" },
 210    { @"entries.0.rating.value", @"3" },
 211    { @"entries.0.rating.average", @"2" },
 212    { @"entries.0.review", @"Ageless? No way." },
 213    { @"entries.0.contentVersion", @"1.2beta5" },
 214    { @"", @"" },
 215
 216    { nil, nil }
 217  };
 218  
 219  [self runTests:tests];
 220}
 221
 222- (void)testCalendarFeed {
 223  
 224  TestKeyPathValues tests[] =
 225  { 
 226    //
 227    // Calendar Feed
 228    //
 229    { @"GDataFeedCalendar/2.0", @"Tests/FeedCalendarTest1.xml" },
 230      
 231    // GDataFeedCalendar paths 
 232    { @"title", @"Fred Flintstone's Calendar List" },
 233    { @"links.1.rel", kGDataLinkRelPost },
 234    { @"links.2.rel", @"self" },
 235    { @"authors.0.name", @"Fred Flintstone" },
 236    { @"authors.0.email", @"fred@gmail.com" },
 237    { @"generator.URI", @"http://www.google.com/calendar" },
 238    { @"generator.name", @"Google Calendar" },
 239    { @"generator.version", @"1.0" },
 240    { @"startIndex", @"1" },
 241    { @"itemsPerPage", @"3" },
 242    { @"ETag", @"Nofzeigeritznum" },
 243    { @"fieldSelection", @"@gd:*,link" },
 244      
 245    { @"unknownAttributes.@count", @"0" },
 246    { @"unknownChildren.@count", @"0" },
 247      
 248    // GDataEntryCalendar paths
 249    { @"entries.0.identifier", @"http://www.google.com/calendar/feeds/test%40domain.net/test%40domain.net" },
 250    { @"entries.0.publishedDate.RFC3339String", @"2006-11-14T00:03:38Z" },
 251    { @"entries.0.updatedDate.RFC3339String", @"2006-11-09T00:16:10Z" },
 252    { @"entries.0.editedDate.RFC3339String", @"2006-11-09T00:16:15Z" },
 253    { @"entries.0.title", @"Fred Flintstone" },
 254    { @"entries.0.links.0.rel", @"alternate" },
 255    { @"entries.0.links.1.href", @"http://www.google.com/calendar/feeds/test%40domain.net/test%40domain.net" },
 256    { @"entries.0.authors.0.name", @"Fred Flintstone" },
 257    { @"entries.0.authors.0.email", @"fred@gmail.com" },
 258    { @"entries.0.isHidden", @"0" },
 259    { @"entries.0.timeZoneName", @"America/Los_Angeles" },
 260    { @"entries.0.timesCleaned", @"31" },
 261    { @"entries.0.color", @"#B1365F" },
 262    { @"entries.0.accessLevel", kGDataCalendarAccessOwner},
 263    { @"entries.0.overrideName", @"over-ride-name" },
 264    { @"entries.0.ETag", @"W/C04EQXc6fCp7ImA9WxZbGUU." },
 265    { @"entries.0.fieldSelection", @"@gd:*,title,gd:when" },
 266    { @"entries.1.locations.0", @"Joes Pub" },
 267    { @"entries.1.fieldSelection", nil },
 268    { @"entries.2.isSelected", @"0" },
 269    { @"entries.2.isHidden", @"1" },
 270    
 271    { @"entries.0.unknownAttributes.@count", @"0" },
 272    { @"entries.0.unknownChildren.@count", @"0" },
 273
 274    { @"", @"" }, // end of feed
 275    
 276    
 277    //
 278    // CalendarEvent Feed
 279    //
 280    { @"GDataFeedCalendarEvent", @"Tests/FeedCalendarEventTest1.xml" },
 281    
 282    // GDataFeedCalendarEvent paths
 283    { @"title", @"Fred Flintstone" },
 284    { @"subtitle", @"Fred Flintstone" },
 285    { @"links.0.rel", kGDataLinkRelFeed },
 286    { @"links.2.rel", @"self" },
 287    { @"authors.0.name", @"Fred Flintstone" },
 288    { @"authors.0.email", @"fred@gmail.com" },
 289    { @"identifier", @"http://www.google.com/calendar/feeds/test%40gmail.com/private/full" },
 290    { @"namespaces.gCal", kGDataNamespaceGCal },
 291    
 292    { @"generator.URI", @"http://www.google.com/calendar" },
 293    { @"generator.name", @"Google Calendar" },
 294    { @"generator.version", @"1.0" },
 295    { @"startIndex", @"1" },
 296    { @"itemsPerPage", @"100000" },
 297    { @"timeZoneName", @"America/Los_Angeles" },
 298    { @"timesCleaned", @"7" },
 299    
 300    { @"unknownAttributes.@count", @"0" },
 301    { @"unknownChildren.@count", @"0" },
 302    
 303    // GDataEntryCalendarEvent paths
 304    { @"entries.0.identifier", @"contains:i12d4avieju0vogcga72aj3908" },
 305    
 306    { @"entries.0.publishedDate.RFC3339String", @"2006-10-27T22:48:14Z" },
 307    { @"entries.0.updatedDate.RFC3339String", @"2006-11-03T21:17:40Z" },
 308    { @"entries.0.title", @"3 days" },
 309    { @"entries.0.content", @"The description field" },
 310
 311    { @"entries.0.links.0.title", @"alternate" },
 312    { @"entries.0.links.1.rel", @"self" },
 313    { @"entries.0.authors.0.name", @"Fred Flintstone" },
 314    { @"entries.0.authors.0.email", @"fred@gmail.com" },
 315    { @"entries.0.visibility", kGDataEventVisibilityDefault },
 316    { @"entries.0.comment.feedLink.href", @"contains:i12d4avieju0vogcga72aj3908/comments" },
 317    { @"entries.0.shouldSendEventNotifications", @"0" },
 318    { @"entries.0.isQuickAdd", @"0" },
 319    { @"entries.0.transparency", kGDataEventTransparencyOpaque },
 320    { @"entries.0.eventStatus", kGDataEventStatusConfirmed },
 321    { @"entries.0.participants.0.email", @"FredFlintstone@gmail.com" },
 322    { @"entries.0.participants.0.rel", kGDataWhoEventAttendee },
 323    { @"entries.0.participants.0.attendeeStatus", kGDataWhoAttendeeStatusDeclined },
 324    { @"entries.0.participants.1.numberOfAdditionalGuests", @"5" },
 325    { @"entries.0.participants.1.email", @"FredFlintstone@google.com" },
 326    { @"entries.0.participants.2.email", @"freg@gmail.com" },
 327    { @"entries.0.times.0.endTime.RFC3339String", @"2006-11-16" },
 328    { @"entries.0.times.0.reminders.0.minutes", @"10" },
 329    { @"entries.0.locations.0", @"The-where-field" },
 330    { @"entries.0.locations.0.rel", nil },
 331    { @"entries.0.sequenceNumber", @"2" },
 332    { @"entries.0.iCalUID", @"4A24A0FF-EA3A-4839-AA09-F4283CB6D345" },
 333    { @"entries.0.canGuestsModify", @"0" },
 334    { @"entries.0.canGuestsInviteOthers", @"0" },
 335    { @"entries.0.canGuestsSeeGuests", @"0" },
 336    { @"entries.0.canAnyoneAddSelf", @"0" },
 337    { @"entries.1.recurrence", @"hasPrefix:DTSTART;VALUE=DATE:20061120" },
 338    { @"entries.1.reminders.0.minutes", @"10" },
 339    { @"entries.1.isDeleted", @"0" },
 340    { @"entries.3.locations.0", @"Seattle" },
 341    { @"entries.3.isDeleted", @"1" },
 342    { @"entries.3.canGuestsModify", @"1" },
 343    { @"entries.3.canGuestsInviteOthers", @"1" },
 344    { @"entries.3.canGuestsSeeGuests", @"1" },
 345    { @"entries.3.canAnyoneAddSelf", @"1" },
 346    
 347    { @"entries.0.unknownAttributes.@count", @"0" },
 348    { @"entries.0.unknownChildren.@count", @"0" },
 349    
 350    { @"", @"" }, // end of feed
 351
 352    //
 353    // CalendarEvent Feed with no entries
 354    //
 355    { @"GDataFeedCalendarEvent", @"Tests/FeedCalendarEventTest0.xml" },
 356
 357    // GDataFeedCalendarEvent paths
 358    { @"title", @"Fred Flintstone" },
 359    { @"subtitle", @"Fred Flintstone" },
 360    { @"links.0.rel", kGDataLinkRelFeed },
 361    { @"links.2.rel", @"self" },
 362    { @"authors.0.name", @"Fred Flintstone" },
 363    { @"authors.0.email", @"fred@gmail.com" },
 364    { @"identifier", @"http://www.google.com/calendar/feeds/test%40gmail.com/private/full" },
 365    { @"namespaces.gCal", kGDataNamespaceGCal },
 366
 367    { @"generator.URI", @"http://www.google.com/calendar" },
 368    { @"generator.name", @"Google Calendar" },
 369    { @"generator.version", @"1.0" },
 370    { @"startIndex", @"1" },
 371    { @"itemsPerPage", @"100000" },
 372    { @"timeZoneName", @"America/Los_Angeles" },
 373    { @"timesCleaned", @"7" },
 374
 375    { @"unknownAttributes.@count", @"0" },
 376    { @"unknownChildren.@count", @"0" },
 377
 378    // GDataEntryCalendarEvent paths
 379    { @"entries.@count", @"0" },
 380
 381    { @"", @"" }, // end of feed
 382
 383    { nil, nil } // end of test array
 384  };
 385  
 386  [self runTests:tests];
 387}
 388
 389- (void)testContactsFeed {
 390  
 391  TestKeyPathValues tests[] =
 392  { 
 393    //
 394    // Contact Feed
 395    //
 396    { @"GDataFeedContact/2.0", @"Tests/FeedContactTest1.xml" },
 397      
 398    // GDataFeedContact paths 
 399    { @"title", @"Contacts" },
 400    { @"categories.0.term", kGDataCategoryContact },
 401    { @"links.1.rel", kGDataLinkRelPost },
 402      
 403    { @"unknownAttributes.@count.stringValue", @"0" },
 404    { @"unknownChildren.@count.stringValue", @"0" },
 405      
 406    // GDataEntryContact paths
 407    // First entry is real; second entry is deleted
 408    { @"entries.0.identifier", @"contains:9cfaae9" },
 409    { @"entries.0.categories.0.term", kGDataCategoryContact },
 410    { @"entries.0.isDeleted", @"0" },
 411      
 412    { @"entries.0.primaryOrganization.orgName", @"Le Company" },
 413      
 414    { @"entries.0.organizations.0.orgName", @"Le Company" },
 415    { @"entries.0.organizations.0.orgTitle", @"Titularstuff" },
 416    { @"entries.0.organizations.0.label", nil },
 417    { @"entries.0.organizations.0.rel", kGDataContactOther },
 418    { @"entries.0.organizations.0.isPrimary", @"1" },
 419      
 420    { @"entries.0.organizations.1.orgName", @"Deadhead Associates" },
 421    { @"entries.0.organizations.1.orgTitle", @"Groupie" },
 422    { @"entries.0.organizations.1.label", @"DAz" },
 423    { @"entries.0.organizations.1.rel", nil },
 424    { @"entries.0.organizations.1.isPrimary", @"0" },
 425      
 426    { @"entries.0.primaryIMAddress.address", @"fooaimz" },
 427      
 428    { @"entries.0.IMAddresses.0.protocol", kGDataIMProtocolAIM },
 429    { @"entries.0.IMAddresses.0.address", @"fooaimz" },
 430    { @"entries.0.IMAddresses.0.label", @"werkz" },
 431    { @"entries.0.IMAddresses.0.isPrimary", @"1" },
 432      
 433    { @"entries.0.IMAddresses.1.protocol", kGDataIMProtocolMSN },
 434    { @"entries.0.IMAddresses.1.address", @"foomsn" },
 435    { @"entries.0.IMAddresses.1.label", nil },
 436    { @"entries.0.IMAddresses.1.rel", kGDataContactHome },
 437    { @"entries.0.IMAddresses.1.isPrimary", @"0" },
 438      
 439    { @"entries.0.IMAddresses.2.protocol", kGDataIMProtocolGoogleTalk },
 440    { @"entries.0.IMAddresses.2.address", @"foo@gmail.com" },
 441    { @"entries.0.IMAddresses.2.label", nil },
 442    { @"entries.0.IMAddresses.2.rel", kGDataContactOther },
 443    { @"entries.0.IMAddresses.2.isPrimary", @"0" },
 444      
 445    { @"entries.0.IMAddresses.3.protocol", kGDataIMProtocolJabber },
 446    { @"entries.0.IMAddresses.3.address", @"foo@jabber.org" },
 447    { @"entries.0.IMAddresses.3.label", @"jabz" },
 448    { @"entries.0.IMAddresses.3.rel", nil },
 449    { @"entries.0.IMAddresses.3.isPrimary", @"0" },
 450      
 451    { @"entries.0.primaryPhoneNumber.stringValue", @"123-4567" },
 452      
 453    { @"entries.0.phoneNumbers.0.stringValue", @"123-4567" },
 454    { @"entries.0.phoneNumbers.0.label", nil },
 455    { @"entries.0.phoneNumbers.0.rel", kGDataPhoneNumberMobile },
 456    { @"entries.0.phoneNumbers.0.isPrimary", @"1" },
 457      
 458    { @"entries.0.phoneNumbers.1.stringValue", @"333-1414" },
 459    { @"entries.0.phoneNumbers.1.label", @"shoefone" },
 460    { @"entries.0.phoneNumbers.1.rel", nil },
 461    { @"entries.0.phoneNumbers.1.isPrimary", @"0" },
 462      
 463    { @"entries.0.primaryPostalAddress.stringValue", @"123 Lane St" },
 464      
 465    { @"entries.0.postalAddresses.0.stringValue", @"123 Lane St" },
 466    { @"entries.0.postalAddresses.0.label", nil },
 467    { @"entries.0.postalAddresses.0.rel", kGDataContactHome },
 468    { @"entries.0.postalAddresses.0.isPrimary", @"1" },
 469      
 470    { @"entries.0.primaryEmailAddress.address", @"foo@bar.com" },
 471      
 472    { @"entries.0.emailAddresses.0.address", @"foo@bar.com" },
 473    { @"entries.0.emailAddresses.0.label", nil },
 474    { @"entries.0.emailAddresses.0.rel", kGDataContactHome },
 475    { @"entries.0.emailAddresses.0.isPrimary", @"1" },
 476    
 477    { @"entries.0.emailAddresses.1.address", @"2@bar.com" },
 478    { @"entries.0.emailAddresses.1.label", @"norzglie" },
 479    { @"entries.0.emailAddresses.1.rel", nil },
 480    { @"entries.0.emailAddresses.1.isPrimary", @"0" },
 481    
 482    { @"entries.0.groupMembershipInfos.0.href", @"http://www.google.com/m8/feeds/contactGroups/user@gmail.com/full/2" },
 483    { @"entries.0.groupMembershipInfos.0.isDeleted", @"1" },
 484      
 485    { @"entries.0.extendedProperties.0.name", @"com.mycompany.myprop" },
 486    { @"entries.0.extendedProperties.0.value", @"zoop" },
 487    { @"entries.0.extendedProperties.0.XMLValues", nil },
 488    
 489    { @"entries.0.extendedProperties.1.name", @"com.mycompany.myprop2" },
 490    { @"entries.0.extendedProperties.1.value", nil },
 491    { @"entries.0.extendedProperties.1.XMLValues.0.XMLString", @"<myXML><myChild attr=\"nerf\"></myChild></myXML>" },
 492    { @"entries.0.extendedProperties.1.unknownChildren.@count.stringValue", @"0" },
 493    
 494    { @"entries.1.identifier", @"contains:b001135" },
 495    { @"entries.1.categories.0.term", kGDataCategoryContact },
 496    { @"entries.1.isDeleted", @"1" },
 497      
 498    { @"entries.0.unknownAttributes.@count.stringValue", @"0" },
 499    { @"entries.0.unknownChildren.@count.stringValue", @"0" },
 500      
 501    { @"", @"" }, // end of feed
 502    
 503    //
 504    // Contact Feed with V3 elements
 505    //
 506    { @"GDataFeedContact/3.0", @"Tests/FeedContactTest2.xml" },
 507
 508    // GDataFeedContact paths
 509    { @"title", @"Fred Flintstone's Contacts" },
 510    { @"categories.0.term", kGDataCategoryContact },
 511    { @"ETag", @"W/\"DkYHQHgzfCt7ImA9WxJREEU.\"" },
 512
 513    { @"unknownAttributes.@count.stringValue", @"0" },
 514    { @"unknownChildren.@count.stringValue", @"0" },
 515
 516    // GDataEntryContact paths
 517    { @"entries.0.identifier", @"contains:754fdf0c0db53ab3" },
 518    { @"entries.0.categories.0.term", kGDataCategoryContact },
 519    { @"entries.0.isDeleted", @"0" },
 520    { @"entries.0.ETag", @"\"Rno_eTVSLyt7ImA9WxJREEUORwc.\"" },
 521    { @"entries.0.editedDate.RFC3339String", @"2009-05-11T23:20:37Z" },
 522
 523    { @"entries.0.primaryOrganization.orgName", @"Acme Corp." },
 524
 525    { @"entries.0.organizations.0.orgName", @"Acme Corp." },
 526    { @"entries.0.organizations.0.orgTitle", nil },
 527    { @"entries.0.organizations.0.label", nil },
 528    { @"entries.0.organizations.0.rel", kGDataContactWork },
 529    { @"entries.0.organizations.0.isPrimary", @"1" },
 530
 531    { @"entries.0.primaryIMAddress.address", nil },
 532
 533    { @"entries.0.IMAddresses.0.protocol", kGDataIMProtocolGoogleTalk },
 534    { @"entries.0.IMAddresses.0.address", @"fredsim@example.com" },
 535    { @"entries.0.IMAddresses.0.label", @"main messaging addr" },
 536    { @"entries.0.IMAddresses.0.rel", nil },
 537    { @"entries.0.IMAddresses.0.isPrimary", @"0" },
 538
 539    { @"entries.0.primaryPhoneNumber.stringValue", @"425-555-1234" },
 540
 541    { @"entries.0.phoneNumbers.0.stringValue", @"425-555-1234" },
 542    { @"entries.0.phoneNumbers.0.label", @"Grand Central" },
 543    { @"entries.0.phoneNumbers.0.rel", nil },
 544    { @"entries.0.phoneNumbers.0.isPrimary", @"1" },
 545
 546    { @"entries.0.phoneNumbers.1.stringValue", @"425-555-0000" },
 547    { @"entries.0.phoneNumbers.1.label", nil },
 548    { @"entries.0.phoneNumbers.1.rel", kGDataPhoneNumberCar },
 549    { @"entries.0.phoneNumbers.1.isPrimary", @"0" },
 550
 551    { @"entries.0.primaryStructuredPostalAddress.street", @"301 Cobblestone Way" },
 552
 553    { @"entries.0.structuredPostalAddresses.0.street", @"301 Cobblestone Way" },
 554    { @"entries.0.structuredPostalAddresses.0.postCode", @"12345" },
 555    { @"entries.0.structuredPostalAddresses.0.formattedAddress", @"301 Cobblestone Way\nBedrock, CA 12345\nUnited States" },
 556    { @"entries.0.structuredPostalAddresses.0.region", @"CA" },
 557    { @"entries.0.structuredPostalAddresses.0.countryName", @"United States" },
 558    { @"entries.0.structuredPostalAddresses.0.label", nil },
 559    { @"entries.0.structuredPostalAddresses.0.rel", kGDataContactHome },
 560    { @"entries.0.structuredPostalAddresses.0.isPrimary", @"1" },
 561
 562    { @"entries.0.primaryEmailAddress.address", @"fred@example.com" },
 563
 564    { @"entries.0.emailAddresses.0.address", @"fred@example.com" },
 565    { @"entries.0.emailAddresses.0.label", nil },
 566    { @"entries.0.emailAddresses.0.rel", kGDataContactHome },
 567    { @"entries.0.emailAddresses.0.isPrimary", @"1" },
 568
 569    { @"entries.0.groupMembershipInfos.0.href", @"http://www.google.com/m8/feeds/groups/fredflintstone%40example.com/base/6" },
 570    { @"entries.0.groupMembershipInfos.0.isDeleted", @"0" },
 571
 572    { @"entries.0.extendedProperties.0.name", @"fredprop" },
 573    { @"entries.0.extendedProperties.0.value", @"12345" },
 574    { @"entries.0.extendedProperties.0.XMLValues", nil },
 575
 576    { @"entries.0.billingInformation", @"account overdue" },
 577
 578    { @"entries.0.birthday", @"1990-12-01" },
 579    { @"entries.0.birthdayDate.timeIntervalSince1970", @"660052800" },
 580
 581    { @"entries.0.calendarLinks.0.rel", nil },
 582    { @"entries.0.calendarLinks.0.label", @"full calendar" },
 583    { @"entries.0.calendarLinks.0.href", @"http://www.google.com/calendar/render" },
 584    { @"entries.0.primaryCalendarLink.label", @"full calendar" },
 585
 586    { @"entries.0.directoryServer", @"dir server" },
 587
 588    // { @"entries.0.events.0", @"" },
 589
 590    { @"entries.0.externalIDs.0.label", @"ext id" },
 591    { @"entries.0.externalIDs.0.rel", nil },
 592    { @"entries.0.externalIDs.0.stringValue", @"54321" },
 593
 594    { @"entries.0.gender", @"male" },
 595
 596    { @"entries.0.hobbies.0.stringValue", @"gurgling" },
 597
 598    { @"entries.0.initials", @"F.F." },
 599
 600    { @"entries.0.jots.0.rel", kGDataContactJotHome },
 601    { @"entries.0.jots.0.stringValue", @"1248" },
 602
 603    // { @"entries.0.languages.0", @"" },
 604
 605    { @"entries.0.maidenName", @"Marshovitzky" },
 606
 607    { @"entries.0.mileage", @"42 miles" },
 608
 609    { @"entries.0.name.fullName", @"Fred Flintstone" },
 610    { @"entries.0.name.givenName", @"Fred" },
 611    { @"entries.0.name.familyName", @"Flintstone" },
 612
 613    { @"entries.0.nickname", @"Rocks" },
 614
 615    { @"entries.0.occupation", @"TV Personality" },
 616
 617    { @"entries.0.priority", kGDataContactPriorityLow },
 618
 619    { @"entries.0.relations.0.rel", kGDataContactRelationPartner },
 620    { @"entries.0.relations.0.label", nil },
 621    { @"entries.0.relations.0.stringValue", @"Wilma" },
 622
 623    { @"entries.0.sensitivity", kGDataContactSensitivityNormal },
 624
 625    { @"entries.0.shortName", @"Freddy" },
 626
 627    { @"entries.0.subject", @"subject val" },
 628
 629    { @"entries.0.userDefinedFields.0.key", @"Cat" },
 630    { @"entries.0.userDefinedFields.0.stringValue", @"Cheezeburger" },
 631
 632    { @"entries.0.websiteLinks.0.href", @"http://example.com/site.html" },
 633    { @"entries.0.websiteLinks.0.rel", kGDataContactWebsiteLinkHomePage },
 634    { @"entries.0.websiteLinks.0.label", nil },
 635    { @"entries.0.websiteLinks.0.isPrimary", @"0" },
 636
 637    { @"entries.0.where", @"The Quarry" },
 638
 639    { @"entries.0.unknownAttributes.@count.stringValue", @"0" },
 640    { @"entries.0.unknownChildren.@count.stringValue", @"0" },
 641
 642    { @"", @"" }, // end of feed
 643
 644    { nil, nil } // end of test array
 645  };
 646
 647  [self runTests:tests];
 648}
 649
 650- (void)testSpreadsheetFeeds {
 651  
 652  TestKeyPathValues tests[] =
 653  { 
 654    //
 655    // Spreadsheet feed (list of user's spreadsheets)
 656    //
 657    { @"GDataFeedSpreadsheet", @"Tests/FeedSpreadsheetTest1.xml" },
 658    
 659    // feed paths
 660    { @"identifier", @"http://spreadsheets.google.com/feeds/spreadsheets/private/full" },
 661    { @"links.2.href", @"http://spreadsheets.google.com/feeds/spreadsheets/private/full?tfe=" },
 662    { @"title", @"Available Spreadsheets - test@foo.net" },
 663    
 664    { @"unknownAttributes.@count", @"0" },
 665    { @"unknownChildren.@count", @"0" },
 666
 667    // entry paths
 668    // There is one entry
 669    { @"entries.0.identifier", @"http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04181601172097104111.497668944883620000" },
 670    { @"entries.0.updatedDate.RFC3339String", @"2007-03-22T23:25:53Z" },
 671    { @"entries.0.categories.2.scheme", kGDataCategorySchemeSpreadsheet },
 672    { @"entries.0.categories.2.term", kGDataCategorySpreadsheet },
 673    { @"entries.0.title", @"My Test Spreadsheet" },
 674
 675    { @"entries.0.unknownAttributes.@count", @"0" },
 676    { @"entries.0.unknownChildren.@count", @"1" },
 677
 678    { @"", @"" }, // end of feed
 679
 680    // repeat the test, with unknown children turned off
 681    { @"GDataFeedSpreadsheet-ignoreUnknown", @"Tests/FeedSpreadsheetTest1.xml" },
 682
 683    // feed paths
 684    { @"identifier", @"http://spreadsheets.google.com/feeds/spreadsheets/private/full" },
 685
 686    { @"unknownAttributes.@count", @"0" },
 687    { @"unknownChildren.@count", @"0" },
 688
 689    // entry paths
 690    // There is one entry
 691    { @"entries.0.identifier", @"http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04181601172097104111.497668944883620000" },
 692    { @"entries.0.updatedDate.RFC3339String", @"2007-03-22T23:25:53Z" },
 693    
 694    { @"entries.0.unknownAttributes.@count", @"0" },
 695    { @"entries.0.unknownChildren.@count", @"0" },
 696    
 697    { @"", @"" }, // end of feed
 698    
 699    //
 700    // Worksheet feed (list of a spreadsheet's worksheets)
 701    //
 702    { @"GDataFeedWorksheet", @"Tests/FeedSpreadsheetWorksheetTest1.xml" },
 703      
 704    // feed paths
 705    { @"identifier", @"http://spreadsheets.google.com/feeds/worksheets/o04181601172097104111.497668944883620000/private/full" },
 706    { @"links.2.href", @"http://spreadsheets.google.com/feeds/worksheets/o04181601172097104111.497668944883620000/private/full?tfe=" },
 707    { @"title", @"My Test Spreadsheet" },
 708    { @"authors.0.email", @"test@foo.net" },
 709        
 710    { @"unknownAttributes.@count", @"0" },
 711    { @"unknownChildren.@count", @"0" },
 712
 713    // entry paths
 714    // There is one entry
 715    { @"entries.0.identifier", @"http://spreadsheets.google.com/feeds/worksheets/o04181601172097104111.497668944883620000/private/full/od6" },
 716    { @"entries.0.updatedDate.RFC3339String", @"2007-03-22T23:28:50Z" },
 717    { @"entries.0.categories.0.scheme", kGDataCategorySchemeSpreadsheet },
 718    { @"entries.0.categories.0.term", kGDataCategoryWorksheet },
 719    { @"entries.0.title", @"Sheet1" },
 720    { @"entries.0.rowCount", @"100" },
 721    { @"entries.0.columnCount", @"20" },
 722    
 723    { @"entries.0.unknownAttributes.@count", @"0" },
 724    { @"entries.0.unknownChildren.@count", @"0" },
 725      
 726    { @"", @"" }, // end of feed
 727 
 728    //
 729    // Cells feed (all of a worksheet's cells)
 730    //
 731    { @"GDataFeedSpreadsheetCell", @"Tests/FeedSpreadsheetCellsTest1.xml" },
 732    
 733    // feed paths
 734    { @"identifier", @"http://spreadsheets.google.com/feeds/cells/o04181601172097104111.497668944883620000/od6/private/full" },
 735    { @"links.0.href", @"http://spreadsheets.google.com/ccc?key=o04181601172097104111.497668944883620000" },
 736    { @"categories.0.scheme", kGDataCategorySchemeSpreadsheet },
 737    { @"categories.0.term", kGDataCategorySpreadsheetCell },
 738    { @"title", @"Sheet1" },
 739    { @"authors.0.email", @"test@foo.net" },
 740    { @"rowCount", @"100" },
 741    { @"columnCount", @"20" },
 742  
 743    { @"unknownAttributes.@count", @"0" },
 744    { @"unknownChildren.@count", @"0" },
 745
 746    // entry paths
 747    // The sheet looks like this (2 cols x 4 rows)
 748    // Fred	Martha
 749    // =pi()	    =sin(A2)
 750    // =1.5*pi()	=sin(A3)
 751    // =2.0*pi()	=sin(A4)
 752    
 753    { @"entries.1.identifier", @"http://spreadsheets.google.com/feeds/cells/o04181601172097104111.497668944883620000/od6/private/full/R1C2" },
 754    { @"entries.1.updatedDate.RFC3339String", @"2007-03-22T23:28:50Z" },
 755    { @"entries.1.categories.0.scheme", kGDataCategorySchemeSpreadsheet },
 756    { @"entries.1.categories.0.term", kGDataCategorySpreadsheetCell },
 757    { @"entries.1.title", @"B1" },
 758    { @"entries.1.cell.column", @"2" },
 759    { @"entries.1.cell.row", @"1" },
 760    { @"entries.1.cell.inputString", @"Martha" },
 761    { @"entries.1.cell.numericValue", nil },
 762    { @"entries.1.cell.resultString", @"Martha" },
 763    
 764    { @"entries.0.unknownAttributes.@count", @"0" },
 765    { @"entries.0.unknownChildren.@count", @"0" },
 766
 767    { @"", @"" }, // end of feed
 768
 769    //
 770    // List feed (all of a worksheet as a list) which contains customElements
 771    //
 772    
 773    // feed paths
 774    { @"GDataFeedSpreadsheetList", @"Tests/FeedSpreadsheetListTest1.xml" },
 775    { @"identifier", @"http://spreadsheets.google.com/feeds/list/o04181601172097104111.497668944883620000/od6/private/full" },
 776    { @"links.0.href", @"http://spreadsheets.google.com/ccc?key=o04181601172097104111.497668944883620000" },
 777    { @"categories.0.scheme", kGDataCategorySchemeSpreadsheet },
 778    { @"categories.0.term", kGDataCategorySpreadsheetList },
 779    { @"title", @"Sheet1" },
 780    { @"authors.0.email", @"test@foo.net" },
 781    
 782    { @"unknownAttributes.@count", @"0" },
 783    { @"unknownChildren.@count", @"0" },
 784
 785    // entry paths
 786    { @"entries.1.customElementDictionary.fred", @"4.71238898038469" },
 787    { @"entries.1.customElementDictionary.martha", @"-1" },
 788    
 789    { @"entries.0.unknownAttributes.@count", @"0" },
 790    { @"entries.0.unknownChildren.@count", @"0" },
 791
 792    { @"", @"" }, // end of feed
 793
 794    //
 795    // Table feed
 796    //
 797
 798    // feed paths
 799    { @"GDataFeedSpreadsheetTable", @"Tests/FeedSpreadsheetTableTest1.xml" },
 800    { @"identifier", @"http://spreadsheets.google.com/feeds/RRHuSwAKiaEGw526z3DVYw/tables" },
 801    { @"categories.0.scheme", kGDataCategoryScheme },
 802    { @"categories.0.term", kGDataCategorySpreadsheetTable },
 803    { @"title", @"Table of Doom" },
 804    { @"authors.0.email", @"fredflintstone@example.com" },
 805
 806    { @"unknownAttributes.@count", @"0" },
 807    { @"unknownChildren.@count", @"0" },
 808
 809    // entry paths
 810    { @"entries.0.content.sourceURI", @"http://spreadsheets.google.com/feeds/RRHuSwAKiaEGw526z3DVYw/records/1" },
 811    { @"entries.0.spreadsheetData.startIndex", @"4" },
 812    { @"entries.0.spreadsheetData.numberOfRows", @"3" },
 813    { @"entries.0.spreadsheetData.insertionMode", kGDataSpreadsheetModeInsert },
 814    { @"entries.0.spreadsheetData.columns.0.name", @"Column Beta" },
 815    { @"entries.0.spreadsheetData.columns.0.indexString", @"B" },
 816    { @"entries.0.spreadsheetHeader.row", @"3" },
 817    { @"entries.0.worksheetName", @"Sheet 1" },
 818
 819    { @"entries.0.unknownAttributes.@count", @"0" },
 820    { @"entries.0.unknownChildren.@count", @"0" },
 821
 822    { @"", @"" }, // end of feed
 823
 824    //
 825    // Record feed
 826    //
 827
 828    // feed paths
 829    { @"GDataFeedSpreadsheetRecord", @"Tests/FeedSpreadsheetRecordTest1.xml" },
 830    { @"identifier", @"http://spreadsheets.google.com/feeds/RRHuSwAKiaEGw526z3DVYw/records/1" },
 831    { @"categories.0.scheme", kGDataCategoryScheme },
 832    { @"categories.0.term", kGDataCategorySpreadsheetRecord },
 833    { @"title", @"Records of Doom" },
 834    { @"authors.0.email", @"fredflintstone@example.com" },
 835
 836    { @"unknownAttributes.@count", @"0" },
 837    { @"unknownChildren.@count", @"0" },
 838
 839    // entry paths
 840    { @"entries.0.content", @"Column Beta: clouds, Column Alpha: mars" },
 841    { @"entries.0.editLink.href", @"http://spreadsheets.google.com/feeds/RRHuSwAKiaEGw526z3DVYw/records/1/cn6ca" },
 842    { @"entries.0.fields.0.name", @"Column Beta" },
 843    { @"entries.0.fields.0.indexString", @"B" },
 844    { @"entries.0.fields.0.value", @"clouds" },
 845    { @"entries.0.fields.0.formula", @"3+something" },
 846
 847    { @"entries.0.unknownAttributes.@count", @"0" },
 848    { @"entries.0.unknownChildren.@count", @"0" },
 849
 850    { @"", @"" }, // end of feed
 851
 852    { nil, nil } // end of test array
 853  };
 854
 855  [self runTests:tests];
 856}
 857
 858- (void)testAnalyticsFeeds {
 859
 860  TestKeyPathValues tests[] =
 861  {
 862    //
 863    // Account feed
 864    //
 865    { @"GDataFeedAnalyticsAccount/2.0", @"Tests/FeedAnalyticsAccountTest1.xml" },
 866
 867    // GDataFeedAnalyticsAccount paths
 868    { @"authors.0.name", @"Google Analytics" },
 869    { @"title", @"Profile list for fredflintstone@example.com" },
 870    { @"segments.0.name", @"All Visits" },
 871    { @"segments.0.analyticsID", @"gaid::-1" },
 872    { @"segments.0.definition", @"" },
 873    { @"segments.1.definition", @"ga:visitorType==New Visitor" },
 874
 875    // GDataEntryAnalyticsAccount paths
 876    { @"entries.0.tableID", @"ga:7966084" },
 877    { @"entries.0.analyticsProperties.0.name", @"ga:accountId" },
 878    { @"entries.0.analyticsProperties.0.stringValue", @"8925159" },
 879    { @"entries.0.analyticsProperties.1.name", @"ga:accountName" },
 880    { @"entries.0.analyticsProperties.1.stringValue", @"example" },
 881    { @"entries.0.customVariables.0.index", @"3" },
 882    { @"entries.0.customVariables.0.name", @"User" },
 883    { @"entries.0.customVariables.0.scope", @"visitor" },
 884    { @"entries.0.goals.0.isActive", @"1" },
 885    { @"entries.0.goals.0.name", @"My Main Goal" },
 886    { @"entries.0.goals.0.number", @"1" },
 887    { @"entries.0.goals.0.value", @"1.100000023841858" },
 888    { @"entries.0.goals.0.destination.isCaseSensitive", @"0" },
 889    { @"entries.0.goals.0.destination.expression", @"/wiki/DocPage" },
 890    { @"entries.0.goals.0.destination.matchType", @"head" },
 891    { @"entries.0.goals.0.destination.isStep1Required", @"0" },
 892    { @"entries.0.goals.0.destination.steps.0.name", @"Wiki step" },
 893    { @"entries.0.goals.0.destination.steps.0.number", @"1" },
 894    { @"entries.0.goals.0.destination.steps.0.path", @"/wiki" },
 895    { @"entries.0.goals.0.engagement", nil },
 896
 897    { @"", @"" }, // end of feed
 898
 899    //
 900    // Data feed
 901    //
 902    { @"GDataFeedAnalyticsData/2.0", @"Tests/FeedAnalyticsDataTest1.xml" },
 903
 904    // GDataFeedAnalyticsData paths
 905    { @"authors.0.name", @"Google Analytics" },
 906    { @"title", @"Google Analytics Data for Profile 7966084" },
 907    { @"startDateString", @"2009-05-18" },
 908    { @"endDateString", @"2009-05-20" },
 909    { @"aggregateGroup.metrics.0.confidenceInterval", @"0" },
 910    { @"aggregateGroup.metrics.0.name", @"ga:pageviews" },
 911    { @"aggregateGroup.metrics.0.type", kGDataMetricTypeInteger },
 912    { @"aggregateGroup.metrics.0.stringValue", @"12" },
 913    { @"aggregateGroup.metrics.0.doubleValue", @"12" },
 914    { @"dataSources.0.tableID", @"ga:7966084" },
 915    { @"dataSources.0.tableName", @"www.example.net" },
 916    { @"dataSources.0.analyticsProperties.0.name", @"ga:profileId" },
 917    { @"dataSources.0.analyticsProperties.0.stringValue", @"7966084" },
 918    { @"dataSources.0.analyticsPropertyWithNameAccountName.stringValue", @"example" },
 919    { @"segments.0.name", @"New Visitors" },
 920    { @"segments.0.analyticsID", @"gaid::-2" },
 921    { @"segments.0.definition", @"ga:visitorType==New Visitor" },
 922
 923    // GDataEntryAnalyticsData paths
 924    { @"entries.0.title", @"ga:country=United States" },
 925    { @"entries.0.dimensions.0.name", @"ga:country" },
 926    { @"entries.0.dimensions.0.stringValue", @"United States" },
 927    { @"entries.0.dimensionWithNameCountry.stringValue", @"United States" },
 928    { @"entries.0.metrics.0.confidenceInterval", @"5.1" },
 929    { @"entries.0.metrics.0.name", @"ga:pageviews" },
 930    { @"entries.0.metrics.0.type", kGDataMetricTypeInteger },
 931    { @"entries.0.metrics.0.stringValue", @"37" },
 932    { @"entries.0.metrics.0.doubleValue", @"37" },
 933    { @"entries.0.metricWithNamePageviews.stringValue", @"37" },
 934
 935    { @"", @"" }, // end of feed
 936
 937    { nil, nil } // end of test array
 938  };
 939
 940  [self runTests:tests];
 941}
 942
 943
 944- (void)testCodeSearchFeed {
 945  
 946  TestKeyPathValues tests[] =
 947  { 
 948    //
 949    // Feed of a user's albums
 950    //
 951    { @"GDataFeedCodeSearch", @"Tests/FeedCodeSearchTest1.xml" },
 952      
 953    // GDataFeedCodeSearch paths
 954    { @"authors.0.name", @"Google Code Search" },
 955    { @"authors.0.URI", @"http://www.google.com/codesearch" },
 956
 957    // GDataEntryCodeSearch paths
 958    { @"entries.0.package.name", @"http://ftp.funet.fi/pub/CPAN/src/perl-5.9.1.tar.gz" },
 959    { @"entries.0.package.URI", @"http://ftp.funet.fi/pub/CPAN/src/perl-5.9.1.tar.gz" },
 960
 961    { @"entries.1.package.name", @"http://gentoo.osuosl.org/distfiles/Perl6-Pugs-6.2.12.tar.gz" },
 962    { @"entries.1.package.URI", @"http://gentoo.osuosl.org/distfiles/Perl6-Pugs-6.2.12.tar.gz" },
 963    { @"entries.1.file.name", @"Perl6-Pugs-6.2.12/t/subroutines/sub_named_params.t" },
 964    { @"entries.1.matches.0.lineNumberString", @"131" },
 965    { @"entries.1.matches.0.type", @"text/html" },
 966    { @"entries.1.matches.0", @"hasPrefix:<pre>my %fellowship" },
 967    { @"entries.1.matches.1.lineNumberString", @"132" },
 968    { @"entries.1.matches.1.type", @"text/html" },
 969    { @"entries.1.matches.1", @"hasPrefix:<pre>is(%fellowship&lt;hobbit&gt;" },
 970      
 971    { @"", @"" }, // end of feed
 972      
 973    { nil, nil } // end of test array
 974  };
 975  
 976  [self runTests:tests];
 977}
 978
 979- (void)testPhotosFeeds {
 980  
 981  // TODO: test geoLocation once we have a good sample of it
 982  
 983  // Test a non-ASCII character and some html characters in a TextConstruct.  
 984  // We'll allocate it dynamically since source code cannot contain non-ASCII.
 985  NSString *templateStr = @"Test %C Alb%Cm";
 986  NSString *photoAlbumName = [NSString stringWithFormat:templateStr, 
 987                              0x262F, 0x00FC]; // yin yang, u with umlaut
 988  
 989  // Non-ascii photo description, includes the Wheel of Dharma
 990  NSString *photoDescriptionText = [NSString stringWithFormat:
 991                                    @"Caption for the car %C photo", 0x2638];  
 992
 993  TestKeyPathValues tests[] =
 994  { 
 995    //
 996    // Feed of a user's albums
 997    //
 998    { @"GDataFeedPhotoUser", @"Tests/FeedPhotosUserAlbum1.xml" },
 999
1000    // GDataFeedPhotosAlbum paths
1001    { @"username", @"TestdomainTestAccount" },
1002    { @"nickname", @"Greg" },
1003    { @"thumbnail", @"hasPrefix:http://lh3.google.com/image/TestdomainTestAccount" },
1004    { @"quotaLimit", @"1073741824" },
1005    { @"quotaUsed", @"108303" },
1006    { @"maxPhotosPerAlbum", @"500" },
1007    { @"categories.0.term", kGDataCategoryPhotosUser },
1008
1009    { @"unknownAttributes.@count", @"0" },
1010    { @"unknownChildren.@count", @"0" },
1011
1012    // GDataEntryPhotoAlbum paths
1013    { @"entries.0.categories.0.term", kGDataCategoryPhotosAlbum },
1014
1015    { @"entries.0.mediaGroup.mediaTitle", photoAlbumName },
1016    { @"entries.0.mediaGroup.mediaDescription", @"Album description" },
1017    { @"entries.0.mediaGroup.mediaCredits.0", @"Greg" },
1018    { @"entries.0.mediaGroup.mediaContents.0.medium", @"image" },
1019    { @"entries.0.mediaGroup.mediaContents.0.type", @"image/jpeg" },
1020    { @"entries.0.mediaGroup.mediaContents.0.URLString", @"hasPrefix:http://lh5.google.com/image/TestdomainTestAccount" },
1021    { @"entries.0.mediaGroup.mediaThumbnails.0.height", @"160" },
1022    { @"entries.0.mediaGroup.mediaThumbnails.0.URLString", @"hasPrefix:http://lh5.google.com/image/TestdomainTestAccount" },
1023
1024    { @"entries.0.GPhotoID", @"5067143575034336993" },
1025    { @"entries.0.access", @"public" },
1026    { @"entries.0.photosUsed", @"2" },
1027    { @"entries.0.commentCount", @"0" },
1028    { @"entries.0.bytesUsed", @"108303" },
1029    { @"entries.0.nickname", @"Greg" },
1030    { @"entries.0.photosLeft", @"498" },
1031    { @"entries.0.commentsEnabled", @"1" },
1032    { @"entries.0.location", @"Album Site" },
1033    { @"entries.0.timestamp.dateValue.timeIntervalSince1970", @"1179730800" },
1034    { @"entries.0.username", @"TestdomainTestAccount" },
1035    { @"entries.0.identifier", @"http://photos.googleapis.com/data/entry/api/user/TestdomainTestAccount/albumid/5067143575034336993" },
1036    { @"entries.0.title.type", @"text" },
1037    { @"entries.0.title", photoAlbumName },
1038    { @"entries.0.photoDescription", @"Album description" },
1039    { @"entries.0.rightsString", @"public" },
1040
1041    { @"entries.0.unknownAttributes.@count", @"0" },
1042    { @"entries.0.unknownChildren.@count", @"0" },
1043
1044    { @"", @"" }, // end of feed
1045
1046    //
1047    // Feed of an album's photos
1048    //
1049    { @"GDataFeedPhotoAlbum/2.0", @"Tests/FeedPhotosAlbumPhoto1.xml" },
1050
1051    // GDataFeedPhotoAlbum - feed paths
1052    { @"GPhotoID", @"5067143575034336993" },
1053    { @"access", @"public" },
1054    { @"photosUsed", @"2" },
1055    { @"commentCount", @"0" },
1056    { @"bytesUsed", @"108303" },
1057    { @"nickname", @"Greg" },
1058    { @"photosLeft", @"498" },
1059    { @"commentsEnabled", @"1" },
1060    { @"location", @"Album Site" },
1061    { @"timestamp.dateValue.timeIntervalSince1970", @"1179730800" },
1062    { @"username", @"TestdomainTestAccount" },
1063    { @"identifier", @"http://photos.googleapis.com/data/feed/api/user/test%40testdomain.net/albumid/5067143575034336993" },
1064    { @"title.type", @"text" },
1065    { @"title", photoAlbumName },
1066    { @"photoDescription", @"Album description" },
1067    { @"rights", @"public" },
1068    { @"categories.0.term", kGDataCategoryPhotosAlbum },
1069    { @"feedLink.href", @"contains:albumid/5067143575034336993" },
1070    { @"feedLink.rel", kGDataLinkRelFeed },
1071    { @"postLink.href", @"contains:albumid/5067143575034336993" },
1072    { @"postLink.rel", kGDataLinkRelPost },
1073    { @"alternateLink.href", @"contains:/TestAlbM" },
1074    { @"alternateLink.rel", @"alternate" },
1075    { @"selfLink.href", @"contains:test%40testdomain.net/albumid/5067143575034336993?start-index=1" },
1076    { @"selfLink.rel", @"self" },
1077
1078    { @"firstEntry.GPhotoID", @"5067143579329304306" },
1079
1080    { @"unknownAttributes.@count", @"0" },
1081    { @"unknownChildren.@count", @"0" },
1082
1083    // GDataEntryPhoto - entry paths
1084    { @"entries.0.categories.0.term", kGDataCategoryPhotosPhoto },
1085
1086    { @"entries.0.checksum", @"23512309abbs298" },
1087    { @"entries.0.GPhotoID", @"5067143579329304306" },
1088    { @"entries.0.albumID", @"5067143575034336993" },
1089    { @"entries.0.width", @"660" },
1090    { @"entries.0.height", @"433" },
1091    { @"entries.0.videoStatus", kGDataPhotoStatusReady },
1092    { @"entries.0.commentsEnabled", @"1" },
1093    { @"entries.0.size", @"87225" },
1094    { @"entries.0.commentCount", @"1" },
1095    { @"entries.0.timestamp.dateValue.timeIntervalSince1970", @"1179786301" },
1096    { @"entries.0.title", @"Car.jpg" },
1097    { @"entries.0.photoDescription", photoDescriptionText },
1098    { @"entries.0.content.sourceURI", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg" },
1099    { @"entries.0.content.type", @"image/jpeg" },
1100
1101    { @"entries.0.mediaGroup.mediaTitle", @"Car.jpg" },
1102    { @"entries.0.mediaGroup.mediaDescription", photoDescriptionText },
1103    { @"entries.0.mediaGroup.mediaCredits.0", @"Greg" },
1104    { @"entries.0.mediaGroup.mediaContents.0.medium", @"image" },
1105    { @"entries.0.mediaGroup.mediaContents.0.type", @"image/jpeg" },
1106    { @"entries.0.mediaGroup.mediaContents.0.URLString", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg" },
1107    { @"entries.0.mediaGroup.mediaThumbnails.0.height", @"47" },
1108    { @"entries.0.mediaGroup.mediaThumbnails.0.width", @"72" },
1109    { @"entries.0.mediaGroup.mediaThumbnails.0.URLString", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg?imgmax=72" },
1110    { @"entries.0.mediaGroup.mediaKeywords", @"headlight, red car" },
1111
1112    { @"entries.0.EXIFTags.tagDictionary.exposure", @"0.0080" },
1113    { @"entries.0.EXIFTags.tagDictionary.imageUniqueID", @"d8a9e8fd57a384d216f4b2a853d654fc" },
1114
1115    { @"entries.0.editMediaLink.href", @"contains:5067143579329304306" },
1116    { @"entries.0.editMediaLink.type", @"image/jpeg" },
1117    { @"entries.0.alternateLink.href", @"contains:photo#5067143579329304306" },
1118    { @"entries.0.alternateLink.type", kGDataLinkTypeHTML },
1119    { @"entries.0.HTMLLink.href", @"contains:photo#5067143579329304306" },
1120    { @"entries.0.selfLink.href", @"contains:photoid/5067143579329304306" },
1121    { @"entries.0.selfLink.type", kGDataLinkTypeAtom },
1122    { @"entries.0.feedLink.href", @"contains:photoid/5067143579329304306" },
1123    { @"entries.0.feedLink.type", kGDataLinkTypeAtom },
1124
1125    { @"entries.0.unknownAttributes.@count", @"0" },
1126    { @"entries.0.unknownChildren.@count", @"0" },
1127
1128    { @"", @"" }, // end of feed
1129
1130    //
1131    // Feed of a photo's comments
1132    //
1133    { @"GDataFeedPhoto/2.0", @"Tests/FeedPhotosPhotoComment1.xml" },
1134
1135    // GDataFeedPhoto - feed paths
1136    { @"generator.URI", @"http://photos.google.com/" },
1137    { @"generator.name", @"Google Photos" },
1138    { @"generator.version", @"1.00" },
1139    { @"EXIFTags.tagDictionary.exposure", @"0.0080" },
1140    { @"categories.0.term", kGDataCategoryPhotosPhoto },
1141    { @"EXIFTags.tagDictionary.imageUniqueID", @"d8a9e8fd57a384d216f4b2a853d654fc" },
1142    { @"checksum", @"23512309abbs298" },
1143    { @"GPhotoID", @"5067143579329304306" },
1144    { @"albumID", @"5067143575034336993" },
1145    { @"width", @"660" },
1146    { @"height", @"433" },
1147    { @"commentsEnabled", @"1" },
1148    { @"size", @"87225" },
1149    { @"commentCount", @"1" },
1150    { @"timestamp.dateValue.timeIntervalSince1970", @"1179786301" },
1151    { @"title", @"Car.jpg" },
1152    { @"photoDescription", photoDescriptionText },
1153    { @"icon", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg?imgmax=288" },
1154
1155    { @"unknownAttributes.@count", @"0" },
1156    { @"unknownChildren.@count", @"0" },
1157
1158    // GDataEntryPhotoComment - entry paths
1159    { @"entries.0.photoID", @"5067143579329304306" },
1160    { @"entries.0.GPhotoID", @"5067146044640532244" }, 
1161    { @"entries.0.categories.0.term", kGDataCategoryPhotosComment },
1162
1163    { @"entries.0.unknownAttributes.@count", @"0" },
1164    { @"entries.0.unknownChildren.@count", @"0" },
1165
1166    { @"", @"" }, // end of feed
1167
1168    //
1169    // Feed of a user's tags
1170    //
1171
1172    // GDataFeedPhotoUser - feed paths
1173
1174    { @"GDataFeedPhotoUser", @"Tests/FeedPhotosUserTag1.xml" },
1175    { @"username", @"TestdomainTestAccount" },
1176    { @"nickname", @"Greg" },
1177    { @"thumbnail", @"hasPrefix:http://lh3.google.com/image/TestdomainTestAccount" },
1178    { @"quotaLimit", @"1073741824" },
1179    { @"quotaUsed", @"108303" },
1180    { @"maxPhotosPerAlbum", @"500" },
1181    { @"categories.0.term", kGDataCategoryPhotosUser },
1182
1183    { @"unknownAttributes.@count", @"0" },
1184    { @"unknownChildren.@count", @"0" },
1185
1186    // GDataEntryPhotoTag - entry paths
1187    
1188    { @"entries.0.title", @"headlight" },
1189    { @"entries.0.photoDescription", @"headlight" }, 
1190    { @"entries.0.categories.0.term", kGDataCategoryPhotosTag },
1191
1192    { @"entries.0.unknownAttributes.@count", @"0" },
1193    { @"entries.0.unknownChildren.@count", @"0" },
1194
1195    { @"entries.1.title", @"red car" },
1196    { @"entries.1.photoDescription", @"red car" }, 
1197    { @"entries.1.categories.0.term", kGDataCategoryPhotosTag },
1198
1199    { @"entries.1.unknownAttributes.@count", @"0" },
1200    { @"entries.1.unknownChildren.@count", @"0" },
1201
1202    { @"", @"" }, // end of feed
1203
1204    //
1205    // Feed of a user entry
1206    //
1207    // This is really a fake feed created by requesting just a single
1208    // user entry from the picasa server, using
1209    // GET http://photos.googleapis.com/data/entry/api/user/<username>
1210    //
1211    
1212    // GDataFeedPhotoUser - feed paths (none)
1213      
1214    // GDataEntryPhotoUser - entry paths
1215    { @"GDataFeedPhotoUser", @"Tests/FeedPhotosUserEntry1.xml" },
1216
1217    { @"entries.0.nickname", @"Greg" },
1218    { @"entries.0.username", @"TestdomainTestAccount" }, 
1219    { @"entries.0.thumbnail", @"hasPrefix:http://lh3.google.com/image/TestdomainTestAccount/AAAAUbcFQeo" },
1220    { @"entries.0.identifier", @"http://photos.googleapis.com/data/entry/api/user/TestdomainTestAccount" },
1221    { @"entries.0.categories.0.term", kGDataCategoryPhotosUser },
1222      
1223    { @"entries.0.unknownAttributes.@count",

Large files files are truncated, but you can click here to view the full file