PageRenderTime 455ms CodeModel.GetById 27ms app.highlight 411ms RepoModel.GetById 1ms app.codeStats 1ms

/core/externals/update-engine/externals/gdata-objectivec-client/Source/Tests/GDataFeedTest.m

http://macfuse.googlecode.com/
Objective C | 2215 lines | 1443 code | 472 blank | 300 comment | 25 complexity | d1bd6b03e4ef34515dd9db386e1ba494 MD5 | raw 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 *const templateStr = @"Test %C Alb%Cm";
 986  NSString *photoAlbumName = [NSString stringWithFormat:templateStr,
 987                              (unichar) 0x262F, (unichar) 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",
 992                                    (unichar) 0x2638];
 993
 994  TestKeyPathValues tests[] =
 995  {
 996    //
 997    // Feed of a user's albums
 998    //
 999    { @"GDataFeedPhotoUser", @"Tests/FeedPhotosUserAlbum1.xml" },
1000
1001    // GDataFeedPhotosAlbum paths
1002    { @"username", @"TestdomainTestAccount" },
1003    { @"nickname", @"Greg" },
1004    { @"thumbnail", @"hasPrefix:http://lh3.google.com/image/TestdomainTestAccount" },
1005    { @"quotaLimit", @"1073741824" },
1006    { @"quotaUsed", @"108303" },
1007    { @"maxPhotosPerAlbum", @"500" },
1008    { @"categories.0.term", kGDataCategoryPhotosUser },
1009
1010    { @"unknownAttributes.@count", @"0" },
1011    { @"unknownChildren.@count", @"0" },
1012
1013    // GDataEntryPhotoAlbum paths
1014    { @"entries.0.categories.0.term", kGDataCategoryPhotosAlbum },
1015
1016    { @"entries.0.mediaGroup.mediaTitle", photoAlbumName },
1017    { @"entries.0.mediaGroup.mediaDescription", @"Album description" },
1018    { @"entries.0.mediaGroup.mediaCredits.0", @"Greg" },
1019    { @"entries.0.mediaGroup.mediaContents.0.medium", @"image" },
1020    { @"entries.0.mediaGroup.mediaContents.0.type", @"image/jpeg" },
1021    { @"entries.0.mediaGroup.mediaContents.0.URLString", @"hasPrefix:http://lh5.google.com/image/TestdomainTestAccount" },
1022    { @"entries.0.mediaGroup.mediaThumbnails.0.height", @"160" },
1023    { @"entries.0.mediaGroup.mediaThumbnails.0.URLString", @"hasPrefix:http://lh5.google.com/image/TestdomainTestAccount" },
1024
1025    { @"entries.0.GPhotoID", @"5067143575034336993" },
1026    { @"entries.0.access", @"public" },
1027    { @"entries.0.photosUsed", @"2" },
1028    { @"entries.0.commentCount", @"0" },
1029    { @"entries.0.bytesUsed", @"108303" },
1030    { @"entries.0.nickname", @"Greg" },
1031    { @"entries.0.photosLeft", @"498" },
1032    { @"entries.0.commentsEnabled", @"1" },
1033    { @"entries.0.location", @"Album Site" },
1034    { @"entries.0.timestamp.dateValue.timeIntervalSince1970", @"1179730800" },
1035    { @"entries.0.username", @"TestdomainTestAccount" },
1036    { @"entries.0.identifier", @"http://photos.googleapis.com/data/entry/api/user/TestdomainTestAccount/albumid/5067143575034336993" },
1037    { @"entries.0.title.type", @"text" },
1038    { @"entries.0.title", photoAlbumName },
1039    { @"entries.0.photoDescription", @"Album description" },
1040    { @"entries.0.rightsString", @"public" },
1041
1042    { @"entries.0.unknownAttributes.@count", @"0" },
1043    { @"entries.0.unknownChildren.@count", @"0" },
1044
1045    { @"", @"" }, // end of feed
1046
1047    //
1048    // Feed of an album's photos
1049    //
1050    { @"GDataFeedPhotoAlbum/2.0", @"Tests/FeedPhotosAlbumPhoto1.xml" },
1051
1052    // GDataFeedPhotoAlbum - feed paths
1053    { @"GPhotoID", @"5067143575034336993" },
1054    { @"access", @"public" },
1055    { @"photosUsed", @"2" },
1056    { @"commentCount", @"0" },
1057    { @"bytesUsed", @"108303" },
1058    { @"nickname", @"Greg" },
1059    { @"photosLeft", @"498" },
1060    { @"commentsEnabled", @"1" },
1061    { @"location", @"Album Site" },
1062    { @"timestamp.dateValue.timeIntervalSince1970", @"1179730800" },
1063    { @"username", @"TestdomainTestAccount" },
1064    { @"identifier", @"http://photos.googleapis.com/data/feed/api/user/test%40testdomain.net/albumid/5067143575034336993" },
1065    { @"title.type", @"text" },
1066    { @"title", photoAlbumName },
1067    { @"photoDescription", @"Album description" },
1068    { @"rights", @"public" },
1069    { @"categories.0.term", kGDataCategoryPhotosAlbum },
1070    { @"feedLink.href", @"contains:albumid/5067143575034336993" },
1071    { @"feedLink.rel", kGDataLinkRelFeed },
1072    { @"postLink.href", @"contains:albumid/5067143575034336993" },
1073    { @"postLink.rel", kGDataLinkRelPost },
1074    { @"alternateLink.href", @"contains:/TestAlbM" },
1075    { @"alternateLink.rel", @"alternate" },
1076    { @"selfLink.href", @"contains:test%40testdomain.net/albumid/5067143575034336993?start-index=1" },
1077    { @"selfLink.rel", @"self" },
1078
1079    { @"firstEntry.GPhotoID", @"5067143579329304306" },
1080
1081    { @"unknownAttributes.@count", @"0" },
1082    { @"unknownChildren.@count", @"0" },
1083
1084    // GDataEntryPhoto - entry paths
1085    { @"entries.0.categories.0.term", kGDataCategoryPhotosPhoto },
1086
1087    { @"entries.0.checksum", @"23512309abbs298" },
1088    { @"entries.0.GPhotoID", @"5067143579329304306" },
1089    { @"entries.0.albumID", @"5067143575034336993" },
1090    { @"entries.0.width", @"660" },
1091    { @"entries.0.height", @"433" },
1092    { @"entries.0.videoStatus", kGDataPhotoStatusReady },
1093    { @"entries.0.commentsEnabled", @"1" },
1094    { @"entries.0.size", @"87225" },
1095    { @"entries.0.commentCount", @"1" },
1096    { @"entries.0.timestamp.dateValue.timeIntervalSince1970", @"1179786301" },
1097    { @"entries.0.title", @"Car.jpg" },
1098    { @"entries.0.photoDescription", photoDescriptionText },
1099    { @"entries.0.content.sourceURI", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg" },
1100    { @"entries.0.content.type", @"image/jpeg" },
1101
1102    { @"entries.0.mediaGroup.mediaTitle", @"Car.jpg" },
1103    { @"entries.0.mediaGroup.mediaDescription", photoDescriptionText },
1104    { @"entries.0.mediaGroup.mediaCredits.0", @"Greg" },
1105    { @"entries.0.mediaGroup.mediaContents.0.medium", @"image" },
1106    { @"entries.0.mediaGroup.mediaContents.0.type", @"image/jpeg" },
1107    { @"entries.0.mediaGroup.mediaContents.0.URLString", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg" },
1108    { @"entries.0.mediaGroup.mediaThumbnails.0.height", @"47" },
1109    { @"entries.0.mediaGroup.mediaThumbnails.0.width", @"72" },
1110    { @"entries.0.mediaGroup.mediaThumbnails.0.URLString", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg?imgmax=72" },
1111    { @"entries.0.mediaGroup.mediaKeywords", @"headlight, red car" },
1112
1113    { @"entries.0.EXIFTags.tagDictionary.exposure", @"0.0080" },
1114    { @"entries.0.EXIFTags.tagDictionary.imageUniqueID", @"d8a9e8fd57a384d216f4b2a853d654fc" },
1115
1116    { @"entries.0.editMediaLink.href", @"contains:5067143579329304306" },
1117    { @"entries.0.editMediaLink.type", @"image/jpeg" },
1118    { @"entries.0.alternateLink.href", @"contains:photo#5067143579329304306" },
1119    { @"entries.0.alternateLink.type", kGDataLinkTypeHTML },
1120    { @"entries.0.HTMLLink.href", @"contains:photo#5067143579329304306" },
1121    { @"entries.0.selfLink.href", @"contains:photoid/5067143579329304306" },
1122    { @"entries.0.selfLink.type", kGDataLinkTypeAtom },
1123    { @"entries.0.feedLink.href", @"contains:photoid/5067143579329304306" },
1124    { @"entries.0.feedLink.type", kGDataLinkTypeAtom },
1125
1126    { @"entries.0.unknownAttributes.@count", @"0" },
1127    { @"entries.0.unknownChildren.@count", @"0" },
1128
1129    { @"", @"" }, // end of feed
1130
1131    //
1132    // Feed of a photo's comments
1133    //
1134    { @"GDataFeedPhoto/2.0", @"Tests/FeedPhotosPhotoComment1.xml" },
1135
1136    // GDataFeedPhoto - feed paths
1137    { @"generator.URI", @"http://photos.google.com/" },
1138    { @"generator.name", @"Google Photos" },
1139    { @"generator.version", @"1.00" },
1140    { @"EXIFTags.tagDictionary.exposure", @"0.0080" },
1141    { @"categories.0.term", kGDataCategoryPhotosPhoto },
1142    { @"EXIFTags.tagDictionary.imageUniqueID", @"d8a9e8fd57a384d216f4b2a853d654fc" },
1143    { @"checksum", @"23512309abbs298" },
1144    { @"GPhotoID", @"5067143579329304306" },
1145    { @"albumID", @"5067143575034336993" },
1146    { @"width", @"660" },
1147    { @"height", @"433" },
1148    { @"commentsEnabled", @"1" },
1149    { @"size", @"87225" },
1150    { @"commentCount", @"1" },
1151    { @"timestamp.dateValue.timeIntervalSince1970", @"1179786301" },
1152    { @"title", @"Car.jpg" },
1153    { @"photoDescription", photoDescriptionText },
1154    { @"icon", @"http://lh3.google.com/image/TestdomainTestAccount/RlIcPQ_TFvI/AAAAAAAAAAs/3fvWtQLN3KI/Car.jpg?imgmax=288" },
1155
1156    { @"unknownAttributes.@count", @"0" },
1157    { @"unknownChildren.@count", @"0" },
1158
1159    // GDataEntryPhotoComment - entry paths
1160    { @"entries.0.photoID", @"5067143579329304306" },
1161    { @"entries.0.GPhotoID", @"5067146044640532244" },
1162    { @"entries.0.categories.0.term", kGDataCategoryPhotosComment },
1163
1164    { @"entries.0.unknownAttributes.@count", @"0" },
1165    { @"entries.0.unknownChildren.@count", @"0" },
1166
1167    { @"", @"" }, // end of feed
1168
1169    //
1170    // Feed of a user's tags
1171    //
1172
1173    // GDataFeedPhotoUser - feed paths
1174
1175    { @"GDataFeedPhotoUser", @"Tests/FeedPhotosUserTag1.xml" },
1176    { @"username", @"TestdomainTestAccount" },
1177    { @"nickname", @"Greg" },
1178    { @"thumbnail", @"hasPrefix:http://lh3.google.com/image/TestdomainTestAccount" },
1179    { @"quotaLimit", @"1073741824" },
1180    { @"quotaUsed", @"108303" },
1181    { @"maxPhotosPerAlbum", @"500" },
1182    { @"categories.0.term", kGDataCategoryPhotosUser },
1183
1184    { @"unknownAttributes.@count", @"0" },
1185    { @"unknownChildren.@count", @"0" },
1186
1187    // GDataEntryPhotoTag - entry paths
1188
1189    { @"entries.0.title", @"headlight" },
1190    { @"entries.0.photoDescription", @"headlight" },
1191    { @"entries.0.categories.0.term", kGDataCategoryPhotosTag },
1192
1193    { @"entries.0.unknownAttributes.@count", @"0" },
1194    { @"entries.0.unknownChildren.@count", @"0" },
1195
1196    { @"entries.1.title", @"red car" },
1197    { @"entries.1.photoDescription", @"red car" },
1198    { @"entries.1.categories.0.term", kGDataCategoryPhotosTag },
1199
1200    { @"entries.1.unknownAttributes.@count", @"0" },
1201    { @"entries.1.unknownChildren.@count", @"0" },
1202
1203    { @"", @"" }, // end of feed
1204
1205    //
1206    // Feed of a user entry
1207    //
1208    // This is really a fake feed created by requesting just a single
1209    // user entry from the picasa server, using
1210    // GET http://photos.googleapis.com/data/entry/api/user/<username>
1211    //
1212
1213    // GDataFeedPhotoUser - feed paths (none)
1214
1215    // GDataEntryPhotoUser - entry paths
1216    { @"GDataFeedPhotoUser", @"Tests/FeedPhotosUserEntry1.xml" },
1217
1218    { @"entries.0.nickname", @"Greg" },
1219    { @"entries.0.username", @"TestdomainTestAccount" },
1220    { @"entries.0.thumbnail", @"hasPrefix:http://lh3.google.com/image/TestdomainTestAccount/AAAAUbcFQeo" },
1221    { @"entries.0.identifier", @"http://photos.googleapis.com/data/entry/api/user/TestdomainTestAccount" },
1222    { @"entries.0.categories.0.term", kGDataCategoryPhotosUser },
1223
1224    { @"entries.0.unknownAttributes.@count", @"0" },
1225    { @"entries.0.unknownChildren.@count", @"0" },
1226
1227    { @"", @"" }, // end of feed
1228
1229    //
1230    // Feed of search results
1231    //
1232
1233    // feed paths
1234    { @"GDataFeedPhoto/2.0", @"Tests/FeedPhotosSearch1.xml" },
1235    { @"title", @"Search Results" },
1236
1237    // entry paths
1238    { @"entries.0.title", @"Leangi_Pima_Shetland_Sheepdog_puppies_f2_3d_4.jpg" },
1239    { @"entries.0.GPhotoID", @"5310251879415742162" },
1240    { @"entries.0.albumID", @"5310242183362606433" },
1241    { @"entries.0.albumTitle", @"Shetland Sheepdog - B-litter - Belinda Mali Zmaj & Marvithall Sherwood" },
1242    { @"entries.0.albumDescription", @"Leangi Pima Shetland Sheepdog puppies" },
1243    { @"entries.0.location", @"Belgrade, Serbia, Leangi Pima kennel" },
1244    { @"entries.0.snippet", @"... <b>puppies</b> ..." },
1245    { @"entries.0.snippetType", kGDataPhotoSnippetTypePhotoTags },
1246    { @"entries.0.truncated", @"0" },
1247    { @"", @"" }, // end of feed
1248
1249    { nil, nil } // end of test array
1250  };
1251
1252  [self runTests:tests];
1253}
1254
1255- (void)testWebmasterToolsFeeds {
1256
1257  TestKeyPathValues tests[] =
1258  {
1259    //
1260    // Feed of a user's sites
1261    //
1262    { @"GDataFeedSite", @"Tests/FeedWebmasterToolsSite1.xml" },
1263
1264    { @"identifier", @"http://www.google.com/webmasters/tools/feeds/sites" },
1265
1266    // GDataEntrySite methods
1267
1268    { @"entries.0.categories.0.term", kGDataCategorySiteInfo },
1269    { @"entries.0.isIndexed", @"1" },
1270    { @"entries.0.crawledDate.date.timeIntervalSince1970", @"1206358560" },
1271    { @"entries.0.isVerified", @"1" },
1272    { @"entries.0.verificationMethods.@count", @"2" },
1273    { @"entries.0.verificationMethods.0.type", kGDataSiteVerificationMethodMetatag },
1274    { @"entries.0.verificationMethods.0.isInUse", @"0" },
1275    { @"entries.0.verificationMethods.1.type", kGDataSiteVerificationMethodHTMLPage },
1276    { @"entries.0.verificationMethods.1.isInUse", @"1" },
1277    { @"entries.0.verificationMethods.1.value", @"google28a5eb30440fabf0.html" },
1278    { @"entries.0.verificationMethodInUse.value", @"google28a5eb30440fabf0.html" },
1279    { @"entries.0.verificationEntryLink.href", @"contains:/http%3A%2F%2Fwww.domain.com%2F/verification" },
1280    { @"entries.0.sitemapsEntryLink.href", @"contains:/http%3A%2F%2Fwww.domain.com%2F/sitemaps" },
1281    { @"entries.0.geoLocation", @"US" },
1282    { @"entries.0.preferredDomain", kGDataSiteCrawlPreferredDomainNone },
1283    { @"entries.0.crawlRate", kGDataSiteCrawlRateNormal },
1284    { @"entries.0.hasEnhancedImageSearch", @"1" },
1285    { @"entries.0.entryLinks.@count", @"2" },
1286
1287    { @"", @"" }, // end of feed
1288
1289    //
1290    // Feed of a site's sitemaps
1291    //
1292    { @"GDataFeedSitemap", @"Tests/FeedWebmasterToolsSitemap1.xml" },
1293
1294    { @"identifier", @"contains:http%3A%2F%2Fwww.domain.com%2F/sitemaps" },
1295
1296    { @"sitemapNews.publicationLabels.@count", @"3" },
1297    { @"sitemapNews.publicationLabels.2.stringValue", @"a third publabel" },
1298
1299    { @"sitemapMobile.markupLanguages.@count", @"3" },
1300    { @"sitemapMobile.markupLanguages.2.stringValue", @"cHTML" },
1301
1302    // GDataEntrySitemap methods
1303
1304    { @"entries.0.categories.0.term", kGDataCategorySitemapRegular },
1305    { @"entries.0.sitemapType", @"WEB" },
1306    { @"entries.0.sitemapStatus", @"Pending" },
1307    { @"entries.0.lastDownloadDate", nil },
1308    { @"entries.0.sitemapURLCount", @"99" },
1309
1310    { @"entries.1.categories.0.term", kGDataCategorySitemapMobile },
1311    { @"entries.1.markupLanguage", @"HTML" },
1312    { @"entries.1.sitemapStatus", @"StatusValue" },
1313    { @"entries.1.lastDownloadDate.date.timeIntervalSince1970", @"1163878052" },
1314    { @"entries.1.sitemapURLCount", @"102" },
1315
1316    { @"entries.2.categories.0.term", kGDataCategorySitemapNews },
1317    { @"entries.2.publicationLabel", @"pubLabelValue" },
1318    { @"entries.2.sitemapStatus", @"AnotherStatusValue" },
1319    { @"entries.2.lastDownloadDate.date.timeIntervalSince1970", @"1163878052" },
1320    { @"entries.2.sitemapURLCount", @"102" },
1321
1322    { @"", @"" }, // end of feed
1323
1324    //
1325    // Feed of a site's crawl issues
1326    //
1327    { @"GDataFeedSiteCrawlIssue", @"Tests/FeedWebmasterToolsCrawlIssues1.xml" },
1328
1329    { @"identifier", @"contains:http%3A%2F%2Fwww.example.net%2F/crawlissues" },
1330    { @"title", @"Crawl Issues" },
1331
1332    // GDataEntrySiteCrawlIssue methods
1333    { @"entries.0.crawlType", kGDataSiteCrawlTypeWeb },
1334    { @"entries.0.detectedDate.RFC3339String", @"2008-11-17T01:06:10Z" },
1335    { @"entries.0.detail", @"4xx Error" },
1336    { @"entries.0.issueType", @"http-error" },
1337    { @"entries.0.issueURLString", @"http://example.com/dir/" },
1338    { @"entries.0.issueLinkedFromURLStrings.0", @"http://example.com" },
1339    { @"entries.0.issueLinkedFromURLStrings.0.className", @"__NSCFString" },
1340
1341    { @"", @"" }, // end of feed
1342
1343    //
1344    // Feed of a site's messages
1345    //
1346    { @"GDataFeedSiteMessage", @"Tests/FeedWebmasterToolsMessages1.xml" },
1347    { @"title", @"Messages" },
1348
1349    // GDataEntrySiteNessage methods
1350    { @"entries.0.body", @"Crawl rate change has been requested." },
1351    { @"entries.0.messageDate.RFC3339String", @"2008-09-18T20:49:57Z" },
1352    { @"entries.0.language", @"en" },
1353    { @"entries.0.read", @"1" },
1354    { @"entries.0.subject", @"hasPrefix:Crawl rate change request" },
1355
1356    { @"", @"" }, // end of feed
1357
1358    //
1359    // Feed of a site's keywords
1360    //
1361    { @"GDataFeedSiteKeyword", @"Tests/FeedWebmasterToolsKeywords1.xml" },
1362
1363    { @"identifier", @"contains:http%3A%2F%2Fwww.example.net%2F/keywords" },
1364
1365    { @"keywords.@count", @"4" },
1366    { @"keywordsWithInternalSource.@count", @"1" },
1367    { @"keywords.0.stringValue", @"example net" },
1368    { @"keywords.0.source", kGDataSiteKeywordExternal },
1369    { @"keywordsWithInternalSource.0.stringValue", @"example" },
1370    { @"keywordsWithInternalSource.0.source", kGDataSiteKeywordInternal },
1371
1372    // the keywords feed has no entries
1373    { @"entries", nil },
1374
1375    { @"", @"" }, // end of feed
1376
1377    { nil, nil } // end of test array
1378  };
1379
1380  [self runTests:tests];
1381}
1382
1383
1384- (void)testMessageFeed {
1385
1386  TestKeyPathValues tests[] =
1387  {
1388    //
1389    // Message Feed
1390    //
1391    { @"GDataFeedMessage", @"Tests/FeedMessageTest1.xml" },
1392
1393    // GDataFeedMessage paths
1394    { @"links.0.href", @"hasPrefix:http://www.google.com/calendar/feeds/default" },
1395    { @"categories.0.term", kGDataMessage },
1396
1397    { @"unknownAttributes.@count", @"0" },
1398    { @"unknownChildren.@count", @"0" },
1399
1400    // GDataEntryMessage paths
1401    { @"entries.0.categories.0.term", kGDataMessage },
1402    { @"entries.0.categories.1.term", kGDataMessageSent },
1403    { @"entries.0.identifier", @"http://mymail.example.com/feeds/jo/home/full/e1a2af06df8a563edf9d32ec9fd61e03f7f3b67b" },
1404    { @"entries.0.content", @"Hi, Fritz -- The information you're looking for is on p. 47." },
1405    { @"entries.0.title", @"Re: Info?" },
1406    { @"entries.0.participants.0.rel", kGDataWhoMessageFrom },
1407    { @"entries.0.participants.1.rel", kGDataWhoMessageTo },
1408
1409    { @"entries.0.unknownAttributes.@count", @"0" },
1410    { @"entries.0.unknownChildren.@count", @"0" },
1411
1412    { @"", @"" }, // end of feed
1413
1414    { nil, nil } // end of test array
1415  };
1416
1417  [self runTests:tests];
1418}
1419
1420
1421- (void)testDocListFeed {
1422
1423  TestKeyPathValues tests[] =
1424  {
1425    //
1426    // Docs Feed
1427    //
1428    { @"GDataFeedDocList", @"Tests/FeedDocListTest1.xml" },
1429
1430    { @"identifier", @"http://docs.google.com/feeds/documents/private/full" },
1431
1432    // Docs Entries.  The elements are all standard atom protocol elements,
1433    // so we are just interested that the proper entry class has been
1434    // instantiated
1435    { @"entries.0.className", @"GDataEntryStandardDoc" },
1436    { @"entries.0.categories.0.term", @"http://schemas.google.com/docs/2007#document"},
1437    { @"entries.0.categories.0.label", @"document"},
1438    { @"entries.0.lastViewed.date.timeIntervalSince1970", @"1222905296"},
1439    { @"entries.0.sharedWithMe.date.timeIntervalSince1970", @"1321426505"},
1440    { @"entries.0.lastModifiedByMe.date.timeIntervalSince1970", @"1325716676"},
1441    { @"entries.0.writersCanInvite", @"1"},
1442    { @"entries.0.lastModifiedBy.name", @"Fred Flintstone"},
1443    { @"entries.0.quotaBytesUsed", @"12345"},
1444    { @"entries.0.documentDescription", @"cherry soda"},
1445    { @"entries.0.MD5Checksum", @"2b01142f7481c7b056c4b410d28f33cf"},
1446    { @"entries.0.filename", @"hi mom.txt"},
1447    { @"entries.0.suggestedFilename", @"Hi Mom.txt"},
1448    { @"entries.0.lastCommented", @"2007-08-02T20:09:40Z"},
1449    { @"entries.0.isRemoved", @"1"},
1450    { @"entries.0.changestamp", @"1234"},
1451    { @"entries.0.isHidden", @"1"},
1452
1453    { @"entries.1.className", @"GDataEntrySpreadsheetDoc" },
1454    { @"entries.1.categories.0.term", @"http://schemas.google.com/docs/2007#spreadsheet"},
1455    { @"entries.1.categories.0.label", @"spreadsheet"},
1456
1457    { @"entries.2.className", @"GDataEntryPresentationDoc" },
1458    { @"entries.2.categories.0.term", @"http://schemas.google.com/docs/2007#presentation"},
1459    { @"entries.2.categories.0.label", @"presentation"},
1460    { @"entries.2.isStarred", @"0"},
1461
1462    { @"entries.3.className", @"GDataEntryPDFDoc" },
1463    { @"entries.3.categories.0.term", @"http://schemas.google.com/docs/2007#pdf"},
1464    { @"entries.3.categories.0.label", @"pdf"},
1465    { @"entries.3.isStarred", @"1"},
1466    { @"entries.3.isHidden", @"0"},
1467    { @"entries.3.ACLFeedLink.href", @"contains:pdf54595c7b"},
1468    { @"entries.3.parentLinks.0.href", @"contains:cc5275b740e3" },
1469
1470    { @"entries.4.className", @"GDataEntryFolderDoc" },
1471    { @"entries.4.categories.0.term", @"http://schemas.google.com/docs/2007#folder"},
1472    { @"entries.4.categories.0.label", @"folder"},
1473
1474    { @"entries.0.unknownAttributes.@count", @"0" },
1475    { @"entries.0.unknownChildren.@count", @"0" },
1476
1477    { @"", @"" }, // end of feed
1478
1479    //
1480    // DocList User Metadata Entry
1481    //
1482    { @"GDataEntryDocListMetadata", @"Tests/EntryDocListMetadataTest1.xml" },
1483
1484    { @"identifier", @"http://docs.google.com/feeds/metadata/fredflintstone%40example.net" },
1485    { @"quotaBytesTotal", @"1073741824" },
1486    { @"quotaBytesUsed", @"124432802" },
1487    { @"quotaBytesUsedInTrash", @"89564924" },
1488    { @"exportFormats.0.source", @"presentation" },
1489    { @"exportFormats.0.target", @"text/plain" },
1490    { @"importFormats.1.source", @"application/vnd.sun.xml.writer" },
1491    { @"importFormats.1.target", @"document" },
1492    { @"features.0.featureName", @"ocr" },
1493    { @"features.0.featureRate", nil },
1494    { @"features.1.featureName", @"translation" },
1495    { @"features.1.featureRate", @"2.0" },
1496    { @"maxUploadSizes.0.longLongNumberValue", @"512000" },
1497    { @"maxUploadSizes.0.uploadKind", @"document" },
1498    { @"maxUploadSizeForKindPDF.longLongNumberValue", @"1048571000" },
1499    { @"featureForNameUploadAny.featureName", @"upload_any" },
1500    { @"featureForNameUploadNone", nil },
1501    { @"", @"" }, // end of feed
1502
1503    { nil, nil } // end of test array
1504  };
1505  [self runTests:tests];
1506};
1507
1508- (void)testFinanceFeed {
1509
1510  TestKeyPathValues tests[] =
1511  {
1512    //
1513    // Portfolio feed
1514    //
1515    { @"GDataFeedFinancePortfolio", @"Tests/FeedFinancePortfolioTest1.xml" },
1516
1517    { @"identifier", @"http://finance.google.com/finance/feeds/default/portfolios" },
1518
1519    // Portfolio entry
1520    { @"entries.0.className", @"GDataEntryFinancePortfolio" },
1521    { @"entries.0.positionURL.absoluteString", @"http://finance.google.com/finance/feeds/default/portfolios/1/positions" },
1522    { @"entries.0.portfolioData.currencyCode", @"USD" },
1523    { @"entries.0.portfolioData.gainPercentage", @"10.3" },
1524    { @"entries.0.portfolioData.return1w", @"12" },
1525    { @"entries.0.portfolioData.return1y", @"17" },
1526    { @"entries.0.portfolioData.return3m", @"0" },
1527    { @"entries.0.portfolioData.return3y", @"23" },
1528    { @"entries.0.portfolioData.return4w", @"-41" },
1529    { @"entries.0.portfolioData.return5y", @"10" },
1530    { @"entries.0.portfolioData.returnOverall", @"12" },
1531    { @"entries.0.portfolioData.returnYTD", @"6.7" },
1532
1533    { @"", @"" }, // end of feed
1534
1535    //
1536    // Position feed
1537    //
1538    { @"GDataFeedFinancePosition", @"Tests/FeedFinancePositionTest1.xml" },
1539
1540    { @"identifier", @"http://finance.google.com/finance/feeds/default/portfolios/1/positions" },
1541
1542    // Position entry
1543    { @"entries.0.className", @"GDataEntryFinancePosition" },
1544    { @"entries.0.transactionURL.absoluteString", @"http://finance.google.com/finance/feeds/default/portfolios/1/positions/NASDAQ:GOOG/transactions" },
1545    { @"entries.0.symbol.exchange", @"NASDAQ" },
1546    { @"entries.0.symbol.fullName", @"Google Inc." },
1547    { @"entries.0.symbol.symbol", @"GOOG" },
1548
1549    { @"entries.0.positionData.shares", @"2" },
1550    { @"entries.0.positionData.gainPercentage", @"1.3" },
1551    { @"entries.0.positionData.return1w", @"0.07" },
1552    { @"entries.0.positionData.return1y", @"-10" },
1553    { @"entries.0.positionData.return3m", @"172.1" },
1554    { @"entries.0.positionData.return3y", @"5.4" },
1555    { @"entries.0.positionData.return4w", @"-4" },
1556    { @"entries.0.positionData.return5y", @"11" },
1557    { @"entries.0.positionData.returnOverall", @"12" },
1558    { @"entries.0.positionData.returnYTD", @"13" },
1559    { @"", @"" }, // end of feed
1560
1561    //
1562    // Transaction feed
1563    //
1564    { @"GDataFeedFinanceTransaction", @"Tests/FeedFinanceTransactionTest1.xml" },
1565
1566    { @"identifier", @"http://finance.google.com/finance/feeds/default/portfolios/1/positions/NASDAQ%3AAAPL/transactions" },
1567
1568    // Transaction entry
1569    { @"entries.0.className", @"GDataEntryFinanceTransaction" },
1570
1571    { @"entries.0.transactionData.date.date.timeIntervalSince1970", @"1212364800" },
1572    { @"entries.0.transactionData.shares", @"100" },
1573    { @"entries.0.transactionData.type", @"Buy" },
1574    { @"entries.0.transactionData.notes", @"Astrologer&#39;s advice" },
1575    { @"entries.0.transactionData.commission.moneyWithPrimaryCurrency.amount", @"13.12" },
1576    { @"entries.0.transactionData.commission.moneyWithPrimaryCurrency.currencyCode", @"USD" },
1577    { @"entries.0.transactionData.price.moneyWithPrimaryCurrency.amount", @"120" },
1578    { @"entries.0.transactionData.price.moneyWithPrimaryCurrency.currencyCode", @"USD" },
1579    { @"entries.0.transactionData.price.moneyWithSecondaryCurrency.amount", @"110" },
1580    { @"entries.0.transactionData.price.moneyWithSecondaryCurrency.currencyCode", @"CAD" },
1581    { @"", @"" }, // end of feed
1582
1583    { nil, nil } // end of test array
1584  };
1585  [self runTests:tests];
1586};
1587
1588- (void)testYouTubeFeeds {
1589
1590  TestKeyPathValues tests[] =
1591  {
1592    //
1593    // user profile
1594    //
1595
1596    // note that the user profile "feed" is really an entry returned by the
1597    // YouTube server
1598    { @"GDataEntryYouTubeUserProfile/2.0", @"Tests/FeedYouTubeUserProfile1.xml" },
1599
1600    // entry elements
1601    { @"identifier", @"http://gdata.youtube.com/feeds/api/users/TestAccount" },
1602
1603    // user profile entry elements
1604    { @"className", @"GDataEntryYouTubeUserProfile" },
1605
1606    { @"channelType", @"Standard" },
1607    { @"statistics.lastWebAccess.RFC3339String", @"2008-01-30T11:38:20-08:00" },
1608    { @"statistics.videoWatchCount", @"1" },
1609    { @"statistics.viewCount", @"2" },
1610    { @"statistics.favoriteCount", @"7" },
1611    { @"statistics.totalUploadViews", @"37" },
1612
1613    { @"books", @"Booksz" },
1614    { @"aboutMe", @"Narf narf" },
1615    { @"age", @"49" },
1616    { @"thumbnail.URLString", @"http://i.ytimg.com/vi/Bkhnvn1PIHs/default.jpg" },
1617    { @"company", @"Apple, Google" },
1618    { @"gender", @"m" },
1619    { @"hobbies", @"Hobbiez, and more hobbiez" },
1620    { @"hometown", @"Oakland, CA" },
1621    { @"location", @"Seattle, WA, US" },
1622    { @"movies", @"Moviez" },
1623    { @"music", @"Musicz" },
1624    { @"occupation", @"worker, player" },
1625    { @"relationship", @"open" },
1626    { @"school", @"Wildwood Elementary" },
1627    { @"username", @"TestAccount" },
1628    { @"firstName", @"Test" },
1629    { @"lastName", @"Account" },
1630
1631    { @"feedLinks.@count", @"6" },
1632
1633    { @"unknownAttributes.@count", @"0" },
1634    { @"unknownChildren.@count", @"0" },
1635
1636    { @"", @"" }, // end of feed
1637
1638    //
1639    // video feed
1640    //
1641    { @"GDataFeedYouTubeVideo/2.0", @"Tests/FeedYouTubeVideo1.xml" },
1642
1643    // video entry elements
1644    { @"entries.0.statistics.viewCount", @"177" },
1645    { @"entries.0.comment.feedLink.href", @"http://gdata.youtube.com/feeds/api/videos/Dl643JFJWig/comments" },
1646    { @"entries.0.links.0.youTubeToken.stringValue", @"ZeeToken" },
1647    { @"entries.0.isEmbeddable", @"0" },
1648    { @"entries.0.rating.numberOfLikes", @"100" },
1649    { @"entries.0.rating.numberOfDislikes", @"25" },
1650    { @"entries.0.mediaGroup.duration", @"3266" },
1651    { @"entries.0.mediaGroup.isPrivate", @"1" },
1652    { @"entries.0.mediaGroup.mediaPlayers.0.URLString", @"http://www.youtube.com/watch?v=Dl643JFJWig" },
1653    { @"entries.0.mediaGroup.mediaRatings.0.stringValue", @"1" },
1654    { @"entries.0.mediaGroup.mediaRatings.0.youTubeCountry", @"KR" },
1655    { @"entries.0.mediaGroup.mediaThumbnails.0.height", @"97" },
1656    { @"entries.0.mediaGroup.mediaThumbnails.0.youTubeName", @"abc987" },
1657    { @"entries.0.publicationState.name", @"rejected" },
1658    { @"entries.0.publicationState.reasonCode", @"32" },
1659    { @"entries.0.publicationState.helpURLString", @"http://www.youtube.com/" },
1660    { @"entries.0.publicationState.errorDescription", @"incorrect format" },
1661    { @"entries.0.isIncomplete", @"1" },
1662    { @"entries.0.captionTracksLink.href", @"contains:GppRTi2JGQ8/captions" },
1663    { @"entries.0.videoResponsesLink.href", @"contains:Dl643JFJWig/responses" },
1664    { @"entries.0.accessControls.0.action", kGDataYouTubeAccessControlVideoRespond },
1665    { @"entries.0.accessControls.0.permission", kGDataYouTubeAccessControlPermissionDenied },
1666    { @"entries.0.accessControls.1.action", kGDataYouTubeAccessControlComment },
1667    { @"entries.0.accessControls.1.permission", kGDataYouTubeAccessControlPermissionAllowed },
1668    { @"entries.0.accessControls.1.type", @"group" },
1669    { @"entries.0.accessControls.1.value", @"friends" },
1670
1671    { @"unknownAttributes.@count", @"0" },
1672    { @"unknownChildren.@count", @"0" },
1673
1674    { @"", @"" }, // end of feed
1675
1676    //
1677    // playlist link feed, v2
1678    //
1679
1680    { @"GDataFeedYouTubePlaylistLink/2.0", @"Tests/FeedYouTubePlaylistLink1.xml" },
1681    { @"categories.0.term", kGDataCategoryYouTubePlaylistLink },
1682    { @"totalResults", @"2" },
1683    { @"startIndex", @"1" },
1684    { @"itemsPerPage", @"22" },
1685
1686    { @"entries.0.className", @"GDataEntryYouTubePlaylistLink" },
1687    { @"entries.0.isPrivate", @"1" },
1688    { @"entries.0.categories.0.term", kGDataCategoryYouTubePlaylistLink },
1689    { @"entries.0.categories.1.scheme", kGDataSchemeYouTubeTag },
1690    { @"entries.0.categories.1.term", @"moreplaylisttags" },
1691    { @"entries.0.countHint", @"10" },
1692
1693    { @"entries.1.categories.0.scheme", kGDataSchemeYouTubeTag },
1694    { @"entries.1.categories.0.term", @"PlaylistTags" },
1695    { @"entries.1.categories.1.term", kGDataCategoryYouTubePlaylistLink },
1696    { @"entries.1.isPrivate", @"0" },
1697    { @"entries.1.mediaGroup.mediaThumbnails.0.URLString", @"http://i.ytimg.com/vi/L-PzCdlQ5aY/default.jpg" },
1698
1699    { @"unknownAttributes.@count", @"0" },
1700    { @"unknownChildren.@count", @"0" },
1701
1702    { @"", @"" }, // end of feed
1703
1704    //
1705    // playlist feed
1706    //
1707
1708    { @"GDataFeedYouTubePlaylist", @"Tests/FeedYouTubePlaylist1.xml" },
1709
1710    { @"categories.0.term", kGDataCategoryYouTubePlaylist },
1711    { @"categories.1.scheme", kGDataSchemeYouTubeTag },
1712    { @"categories.1.term", @"PlaylistTags" },
1713    { @"totalResults", @"2" },
1714    { @"startIndex", @"1" },
1715    { @"itemsPerPage", @"25" },
1716
1717    { @"mediaGroup.mediaContents.0.URLString", @"http://www.youtube.com/ep.swf?id=BBA2B78EF2F08B10" },
1718    { @"mediaGroup.mediaContents.0.youTubeFormatNumber", @"5" },
1719
1720    { @"entries.0.className", @"GDataEntryYouTubePlaylist" },
1721    { @"entries.0.categories.0.term", kGDataCategoryYouTubePlaylist },
1722    { @"entries.0.mediaGroup.mediaTitle", @"The Graphing Calculator Story" },
1723    { @"entries.0.mediaGroup.mediaDescription", @"Google TechTalks" },
1724    { @"entries.0.mediaGroup.mediaKeywords.keywords.1", @"avitzur" },
1725    { @"entries.0.mediaGroup.duration", @"3266" },
1726    { @"entries.0.statistics.viewCount", @"185" },
1727    { @"entries.0.position", @"1" },
1728
1729    { @"unknownAttributes.@count", @"0" },
1730    { @"unknownChildren.@count", @"0" },
1731
1732    { @"", @"" }, // end of feed
1733
1734    //
1735    // contacts feed
1736    //
1737    { @"GDataFeedYouTubeFriend", @"Tests/FeedYouTubeContacts1.xml" },
1738
1739    { @"categories.0.term", kGDataCategoryYouTubeFriend },
1740
1741    { @"entries.0.className", @"GDataEntryYouTubeFriend" },
1742
1743    { @"entries.0.username", @"fredflintstone" },
1744    { @"entries.0.status", @"accepted" },
1745
1746    { @"entries.0.unknownAttributes.@count", @"0" },
1747    { @"entries.0.unknownChildren.@count", @"0" },
1748
1749    { @"", @"" }, // end of feed
1750
1751    //
1752    // caption track feed
1753    //
1754    { @"GDataFeedYouTubeCaptionTrack", @"Tests/FeedYouTubeCaptionTracks1.xml" },
1755
1756    { @"categories.0.term", kGDataCategoryYouTubeCaptionTrack },
1757
1758    { @"entries.0.className", @"GDataEntryYouTubeCaptionTrack" },
1759    { @"entries.0.title", @"captrack name" },
1760    { @"entries.0.categories.0.term", kGDataCategoryYouTubeCaptionTrack },
1761
1762    { @"entries.0.publicationState.name", @"failed" },
1763    { @"entries.0.publicationState.reasonCode", @"invalidFormat" },
1764    { @"entries.0.derived", kGDataYouTubeDerivedSpeechRecogniion },
1765
1766    { @"entries.0.unknownAttributes.@count", @"0" },
1767    { @"entries.0.unknownChildren.@count", @"0" },
1768    { @"entries.0.unknownChildren", nil },
1769
1770    { @"", @"" }, // end of feed
1771
1772    //
1773    // subscription feed
1774    //
1775    { @"GDataFeedYouTubeSubscription/2.0", @"Tests/FeedYouTubeSubscriptions1.xml" },
1776
1777    // feed elements
1778    { @"identifier", @"http://gdata.youtube.com/feeds/api/users/testaccount/subscriptions?start-index=1&max-results=25" },
1779    { @"logo", @"http://www.youtube.com/img/pic_youtubelogo_123x63.gif" },
1780
1781    // subscription entry elements
1782    { @"entries.0.className", @"GDataEntryYouTubeSubscription" },
1783
1784    { @"entries.0.subscriptionType", @"channel" },
1785    { @"entries.0.username", @"googletechtalks" },
1786    { @"entries.0.content.sourceURI", @"http://gdata.youtube.com/feeds/api/users/googletechtalks/uploads" },
1787    { @"entries.0.countHint", @"2" },
1788    { @"entries.0.relatedLink.href", @"http://gdata.youtube.com/feeds/api/users/TestAccount" },
1789
1790    { @"entries.1.youTubeQueryString", @"cute dogs" },
1791    { @"entries.1.playlistID", @"1sbd45" },
1792    { @"entries.1.playlistTitle", @"Dog Playlist" },
1793
1794    { @"entries.0.unknownAttributes.@count", @"0" },
1795    { @"entries.0.unknownChildren.@count", @"0" },
1796
1797    { @"", @"" }, // end of feed
1798
1799    //
1800    // favorites feed
1801    //
1802    { @"GDataFeedYouTubeFavorite/2.0", @"Tests/FeedYouTubeFavorites1.xml" },
1803
1804    // feed elements
1805    { @"ETag", @"W/\"CUACQX47eCp7ImA9WxdSEkg.\"" },
1806    { @"identifier", @"tag:youtube,2008:user:fredflintstone:favorites" },
1807    { @"logo", @"http://www.youtube.com/img/pic_youtubelogo_123x63.gif" },
1808
1809    // favorites entry elements
1810    { @"entries.0.className", @"GDataEntryYouTubeFavorite" },
1811    { @"entries.0.ETag", @"W/\"CUACQX47eCp7ImA9WxdSEkg.\"" },
1812    { @"entries.0.identifier", @"tag:youtube,2008:favorite:vjVQa1PpcFP8fBdcAc_YTSRHgC1CE_ageg03_RO2EVk" },
1813    { @"entries.0.content.sourceURI", @"http://www.youtube.com/v/I-t-7lTw6mA&f=gdata_user_favorites" },
1814
1815    { @"entries.0.location", @"mountain view, ca" },
1816    { @"entries.0.recordedDate", @"2008-03-04" },
1817
1818    { @"entries.0.mediaGroup.mediaTitle", @"Authors@Google: Michael Pollan" },
1819    { @"entries.0.mediaGroup.mediaDescription", @"Michael Pollan visits Google" },
1820    { @"entries.0.mediaGroup.videoID", @"I-t-7lTw6mA" },
1821    { @"entries.0.mediaGroup.aspectRatio", @"widescreen" },
1822    { @"entries.0.mediaGroup.uploadedDate.date.timeIntervalSince1970", @"1204847352" },
1823    { @"entries.0.mediaGroup.mediaCredits.0.role", @"uploader" },
1824    { @"entries.0.mediaGroup.mediaCredits.0.scheme", @"urn:youtube" },
1825    { @"entries.0.mediaGroup.mediaCredits.0.youTubeCreditType", @"partner" },
1826    { @"entries.0.mediaGroup.mediaContents.0.duration", @"3554" },
1827    { @"entries.0.mediaGroup.mediaContents.0.expression", @"full" },
1828    { @"entries.0.mediaGroup.mediaContents.0.medium", @"video" },
1829    { @"entries.0.mediaGroup.mediaContents.0.type", @"application/x-shockwave-flash" },
1830    { @"entries.0.mediaGroup.mediaContents.0.URLString", @"http://www.youtube.com/v/I-t-7lTw6mA&f=gdata_user_favorites" },
1831    { @"entries.0.mediaGroup.mediaContents.0.youTubeFormatNumber", @"5" },
1832    { @"entries.0.mediaGroup.highQualityThumbnail.URLString", @"http://i.ytimg.com/vi/I-t-7lTw6mA/0.jpg" },
1833    { @"entries.0.mediaGroup.mediaContentWithFormatNumber1.type", @"video/3gpp" },
1834
1835    { @"entries.0.unknownAttributes.@count", @"0" },
1836    { @"entries.0.unknownChildren.@count", @"0" },
1837
1838    { @"", @"" }, // end of feed
1839
1840    //
1841    // user events feed
1842    //
1843    { @"GDataFeedYouTubeUserEvent/2.0", @"Tests/FeedYouTubeUserEventTest1.xml" },
1844
1845    // feed elements
1846    { @"ETag", @"W/\"AkUBRH47eCp7ImA9WxVWFkU.\"" },
1847    { @"identifier", @"tag:youtube.com,2008:events" },
1848
1849    // entry elements
1850    { @"entries.0.className", @"GDataEntryYouTubeUserEvent" },
1851    { @"entries.0.kindCategory.term", kGDataCategoryYouTubeUserEvent },
1852    { @"entries.0.ETag", @"W/\"AkUBRH47eCp7ImA9WxVWFkU.\"" },
1853    { @"entries.0.identifier", @"tag:youtube.com,2008:event:Z3JlZ3JvYmJpbnM3MTIzNTY4ODI1NUJlbGFuYXI%3D" },
1854    { @"entries.0.username", @"wilmaflintstone" },
1855    { @"entries.0.userEventType", kGDataYouTubeUserEventFriendAdded },
1856    { @"entries.0.unknownAttributes.@count", @"0" },
1857    { @"entries.0.unknownChildren.@count", @"0" },
1858
1859    { @"entries.1.userEventType", kGDataYouTubeUserEventVideoFavorited },
1860    { @"entries.1.videoLink.href", @"http://gdata.youtube.com/feeds/api/videos/cxFfUsDgnaU" },
1861    { @"entries.1.videoID", @"cxFfUsDgnaU" },
1862    { @"entries.1.unknownAttributes.@count", @"0" },
1863    { @"entries.1.unknownChildren.@count", @"0" },
1864
1865    { @"", @"" }, // end of feed
1866
1867    { nil, nil } // end of test array
1868  };
1869  [self runTests:tests];
1870};
1871
1872- (void)testMapsFeed {
1873
1874  TestKeyPathValues tests[] =
1875  {
1876    //
1877    // Map Feature Feed
1878    //
1879
1880    { @"GDataFeedMapFeature/2.0", @"Tests/FeedMapFeaturesTest1.xml" },
1881
1882    // feature feed elements
1883    { @"title", @"Kirkland Office" },
1884    { @"identifier", @"http://maps.google.com/maps/feeds/features/2063624404192119555/0046a899354a8585d4d" },
1885
1886    { @"unknownAttributes.@count", @"0" },
1887    { @"unknownChildren.@count", @"0" },
1888
1889    // feature entry elements
1890    { @"entries.0.editedDate.RFC3339String", @"2009-05-23T02:10:51Z" },
1891    { @"entries.0.categories.0.term", kGDataCategoryMapFeature },
1892
1893    { @"entries.0.KMLValues.@count", @"1" },
1894    { @"entries.0.KMLValues.0.name", @"Placemark" },
1895    { @"entries.0.viewLink.href", @"contains:view/00047730f33bb0097204c" },
1896
1897    { @"entries.0.postalAddress.city", @"Anytown" },
1898
1899    { @"entries.0.customProperties.@count", @"2" },
1900    { @"entries.0.customProperties.0.name", @"distance" },
1901    { @"entries.0.customProperties.0.unit", @"meters" },
1902    { @"entries.0.customProperties.0.value", @"36" },
1903    { @"entries.0.customPropertyWithHeightName.value", @"10.2" },
1904
1905    { @"entries.0.unknownAttributes.@count", @"0" },
1906    { @"entries.0.unknownChildren.@count", @"0" },
1907
1908    { @"", @"" }, // end of feed
1909
1910    { nil, nil } // end of test array
1911  };
1912
1913  [self runTests:tests];
1914}
1915
1916- (void)testBlogFeeds {
1917
1918  TestKeyPathValues tests[] =
1919  {
1920    //
1921    // Blog feed
1922    //
1923
1924    { @"GDataFeedBlog/2.0", @"Tests/FeedBlogTest1.xml" },
1925
1926    // feed elements
1927    { @"title", @"TestAccount's Blogs" },
1928
1929    { @"unknownAttributes.@count", @"0" },
1930    { @"unknownChildren.@count", @"0" },
1931
1932    // entry elements - no extensions, so just test convenience accessors
1933    { @"entries.0.templateLink.href", @"http://www.blogger.com/feeds/145278007186576769/template" },
1934    { @"entries.0.settingsLink.href", @"http://www.blogger.com/feeds/145278007186576769/settings" },
1935
1936    { @"entries.0.unknownAttributes.@count", @"0" },
1937    { @"entries.0.unknownChildren.@count", @"0" },
1938
1939    { @"", @"" }, // end of feed
1940
1941    //
1942    // Blog post feed
1943    //
1944
1945    { @"GDataFeedBlogPost/2.0", @"Tests/FeedBlogPostTest1.xml" },
1946
1947    // feed elements
1948    { @"title", @"Test Posts" },
1949
1950    { @"unknownAttributes.@count", @"0" },
1951    { @"unknownChildren.@count", @"0" },
1952
1953    // entry elements
1954    { @"entries.0.atomPubControl.isDraft", @"1" },
1955    { @"entries.0.identifier", @"tag:blogger.com,1999:blog-1452780071865767697.post-6317479243618470383" },
1956    { @"entries.0.total", @"3" },
1957    { @"entries.0.thumbnail.URLString", @"contains:Hippo.jpg" },
1958    { @"entries.0.repliesHTMLLink.href", @"https://www.blogger.com/comment.g?blogID=1452780071865767697&postID=6317479243618470383" },
1959    { @"entries.0.repliesAtomLink.href", @"http://testaccount.blogspot.com/feeds/6317479243618470383/comments/default" },
1960    { @"entries.0.enclosureLink.href", @"http://example.net/file.txt" },
1961
1962    { @"entries.0.unknownAttributes.@count", @"0" },
1963    { @"entries.0.unknownChildren.@count", @"0" },
1964
1965    { @"", @"" }, // end of feed
1966
1967    //
1968    // Blog comments feed
1969    //
1970
1971    { @"GDataFeedBlogComment/2.0", @"Tests/FeedBlogCommentsTest1.xml" },
1972
1973    // feed elements
1974    { @"title", @"Comments on Post" },
1975
1976    { @"unknownAttributes.@count", @"0" },
1977    { @"unknownChildren.@count", @"0" },
1978
1979    // entry elements
1980    { @"entries.0.inReplyTo.href", @"http://testaccount.blogspot.com/2009/06/post-created-2009-06-02-161600-0700.html" },
1981    { @"entries.0.inReplyTo.ref", @"tag:blogger.com,1999:blog-1452780071865767697.post-1015986829942539734" },
1982    { @"entries.0.inReplyTo.source", @"http://testaccount.blogspot.com/feeds/posts/default/1015986829942539734" },
1983    { @"entries.0.inReplyTo.type", kGDataLinkTypeHTML },
1984
1985    { @"entries.0.unknownAttributes.@count", @"0" },
1986    { @"entries.0.unknownChildren.@count", @"0" },
1987
1988    { @"", @"" }, // end of feed
1989
1990    { nil, nil } // end of test array
1991  };
1992
1993  [self runTests:tests];
1994}
1995
1996- (void)testACLFeed {
1997
1998  TestKeyPathValues tests[] =
1999  {
2000    //
2001    // ACL Feed
2002    //
2003    { @"GDataFeedACL", @"Tests/FeedACLTest1.xml" },
2004
2005    // GDataFeedACL paths
2006    { @"links.0.href", @"http://www.google.com/calendar/feeds/test%40gmail.com/private/full" },
2007    { @"links.0.rel", kGDataLinkRelControlledObject },
2008    { @"categories.0.term", kGDataCategoryACL },
2009    { @"categories.0.scheme", kGDataCategoryScheme },
2010
2011    { @"unknownAttributes.@count", @"0" },
2012    { @"unknownChildren.@count", @"0" },
2013
2014    // GDataEntryACL paths (scope and role are the main elements)
2015    { @"entries.0.categories.0.term", kGDataCategoryACL },
2016    { @"entries.0.identifier", @"http://www.google.com/calendar/feeds/test%40gmail.com/acl/full/user%3Atest%40gmail.com" },
2017    { @"entries.0.content", @"" },
2018    { @"entries.0.links.1.rel", @"edit" },
2019    { @"entries.0.scope.type", @"user" },
2020    { @"entries.0.scope.value", @"test@example.com" },
2021    { @"entries.0.role.value", @"http://schemas.google.com/gCal/2005#owner" },
2022
2023    { @"entries.0.unknownAttributes.@count", @"0" },
2024    { @"entries.0.unknownChildren.@count", @"0" },
2025
2026    { @"entries.1.scope.type", @"user" },
2027    { @"entries.1.scope.name", @"wilma" },
2028    { @"entries.1.keyedRole.key", @"A123B" },
2029    { @"entries.1.keyedRole.role.value", @"reader" },
2030    { @"entries.1.keyedRole.additionalRoles.1.value", @"pigeon" },
2031
2032    { @"entries.1.unknownAttributes.@count", @"0" },
2033    { @"entries.1.unknownChildren.@count", @"0" },
2034
2035    { @"", @"" }, // end of feed
2036
2037    { nil, nil } // end of test array
2038  };
2039
2040  [self runTests:tests];
2041}
2042
2043- (void)testServiceDocuments {
2044
2045  TestKeyPathValues tests[] =
2046  {
2047    //
2048    // service document
2049    //
2050    { @"GDataAtomServiceDocument", @"Tests/FeedServiceDocTest2.xml" },
2051
2052    // GDataAtomServiceDocument paths
2053    { @"namespaces.app", kGDataNamespaceAtomPub },
2054    { @"workspaces.@count", @"2" },
2055
2056    // workspace paths
2057    { @"workspaces.0.title", @"fredflintstone workspace" },
2058    { @"workspaces.0.collections.@count", @"1" },
2059    { @"workspaces.0.collections.0.title", @"filetypes" },
2060    { @"workspaces.0.collections.0.serviceAcceptStrings.@count", @"5" },
2061    { @"workspaces.0.collections.0.serviceAcceptStrings.0", @"image/png" },
2062    { @"workspaces.0.collections.0.serviceAcceptStrings.1", @"image/jpeg" },
2063
2064    { @"workspaces.1.title", @"Second Workspace" },
2065    { @"workspaces.1.collections.@count", @"1" },
2066    { @"workspaces.1.collections.0.href", @"http://example.org/hrefcollection" },
2067    { @"workspaces.1.collections.0.title", @"Remaindered Links" },
2068    { @"workspaces.1.collections.0.serviceAcceptStrings.@count", @"1" },
2069    { @"workspaces.1.collections.0.serviceAcceptStrings.0", @"entry" },
2070    { @"workspaces.1.collections.0.categoryGroup.isFixed", @"1" },
2071    { @"workspaces.1.collections.0.categoryGroup.scheme", @"bogusscheme" },
2072    { @"workspaces.1.collections.0.categoryGroup.href", @"bogushref" },
2073    { @"workspaces.1.collections.0.categoryGroup.categories.@count", @"2" },
2074    { @"workspaces.1.collections.0.categoryGroup.categories.1.term", @"serious" },
2075
2076    { @"unknownAttributes.@count", @"0" },
2077    { @"unknownChildren.@count", @"0" },
2078
2079    { @"", @"" }, // end of feed
2080
2081    { nil, nil } // end of test array
2082  };
2083
2084  [self runTests:tests];
2085}
2086
2087- (void)testDetachedEntries {
2088
2089  // get a feed, retain the entries, release the feed, then verify that the
2090  // entries are usable with the feed itself gone
2091
2092  NSData *data = [NSData dataWithContentsOfFile:@"Tests/FeedCalendarEventTest1.xml"];
2093  STAssertNotNil(data, @"Cannot read feed for detach test");
2094
2095  // create the feed object
2096  GDataFeedBase *feed = [[GDataFeedCalendarEvent alloc] initWithData:data
2097                                                      serviceVersion:@"2.1"
2098                                                shouldIgnoreUnknowns:NO];
2099  STAssertNotNil(feed, @"Cannot make detaching feed");
2100
2101  NSArray *entries = [[[feed entries] retain] autorelease];
2102  STAssertTrue([entries count] > 0, @"Feed lacks entries to detach");
2103
2104  [feed release];
2105
2106  GDataEntryBase *firstEntry = [entries objectAtIndex:0];
2107  NSString *title = [[firstEntry title] stringValue];
2108  STAssertEqualObjects(title, @"3 days", @"testing an element in a detached entry");
2109
2110  NSString *titleType = [[firstEntry title] type];
2111  STAssertEqualObjects(titleType, @"text", @"testing an attribute in a detached entry");
2112}
2113
2114@end
2115
2116///////////////////////////////////////////////////////////////////////////
2117//
2118// categories to test helper methods that require arguments
2119//
2120
2121//
2122// YouTube
2123//
2124
2125@interface GDataYouTubeMediaGroup (TestHelperMethods)
2126@end
2127
2128@implementation GDataYouTubeMediaGroup (TestHelperMethods)
2129- (GDataMediaContent *)mediaContentWithFormatNumber1 {
2130  return [self mediaContentWithFormatNumber:kGDataYouTubeMediaContentFormatRTSPStream];
2131}
2132@end
2133
2134//
2135// analytics
2136//
2137
2138@interface GDataAnalyticsDataSource (TestHelperMethods)
2139- (GDataAnalyticsProperty *)analyticsPropertyWithNameAccountName;
2140@end
2141
2142@implementation GDataAnalyticsDataSource (TestHelperMethods)
2143- (GDataAnalyticsProperty *)analyticsPropertyWithNameAccountName {
2144  return [self analyticsPropertyWithName:@"ga:accountName"];
2145}
2146@end
2147
2148
2149@interface GDataEntryAnalyticsData (TestHelperMethods)
2150- (GDataAnalyticsDimension *)dimensionWithNameCountry;
2151- (GDataAnalyticsMetric *)metricWithNamePageviews;
2152@end
2153
2154@implementation GDataEntryAnalyticsData (TestHelperMethods)
2155- (GDataAnalyticsDimension *)dimensionWithNameCountry {
2156  return [self dimensionWithName:@"ga:country"];
2157}
2158
2159- (GDataAnalyticsMetric *)metricWithNamePageviews {
2160  return [self metricWithName:@"ga:pageviews"];
2161}
2162@end
2163
2164//
2165// maps
2166//
2167
2168@interface GDataEntryMapFeature (TestHelperMethods)
2169- (GDataCustomProperty *)customPropertyWithHeightName;
2170@end
2171
2172@implementation GDataEntryMapFeature (TestHelperMethods)
2173- (GDataCustomProperty *)customPropertyWithHeightName {
2174  return [self customPropertyWithName:@"height"];
2175}
2176@end
2177
2178//
2179// DocList User Metadata
2180//
2181
2182@interface GDataEntryDocListMetadata (TestHelperMethods)
2183- (GDataDocMaxUploadSize *)maxUploadSizeForKindPDF;
2184- (GDataDocFeature *)featureForNameUploadAny;
2185- (GDataDocFeature *)featureForNameUploadNone;
2186@end
2187
2188@implementation GDataEntryDocListMetadata (TestHelperMethods)
2189- (GDataDocMaxUploadSize *)maxUploadSizeForKindPDF {
2190  return [self maxUploadSizeForKind:@"pdf"];
2191}
2192
2193- (GDataDocFeature *)featureForNameUploadAny {
2194  return [self featureForName:@"upload_any"];
2195}
2196
2197- (GDataDocFeature *)featureForNameUploadNone {
2198  return [self featureForName:@"upload_none"];
2199}
2200
2201@end
2202
2203//
2204// webmaster tools
2205//
2206
2207@interface GDataFeedSiteKeyword (TestHelperMethods)
2208- (NSArray *)keywordsWithInternalSource;
2209@end
2210
2211@implementation GDataFeedSiteKeyword (TestHelperMethods)
2212- (NSArray *)keywordsWithInternalSource {
2213  return [self keywordsWithSource:kGDataSiteKeywordInternal];
2214}
2215@end