From b3bc6bb7dc40b002f034493c69b29b3bd3001449 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 18 Jan 2016 09:26:07 -0500 Subject: [PATCH] Applied changes coming from v2/PR#184. --- NEWS | 13 +- OpenChange/MAPIStoreCalendarFolder.m | 15 ++- OpenChange/MAPIStoreSOGo.m | 86 ++++++------- OpenChange/RTFHandler.h | 10 +- OpenChange/RTFHandler.m | 13 ++ SoObjects/Appointments/iCalPerson+SOGo.m | 26 +++- SoObjects/Mailer/SOGoDraftObject.m | 18 +-- SoObjects/SOGo/NSString+Utilities.m | 31 +---- Tests/Unit/GNUmakefile | 12 +- Tests/Unit/SOGoTest.m | 3 +- Tests/Unit/SOGoTestRunner.h | 17 ++- Tests/Unit/SOGoTestRunner.m | 98 +++++++++++++- Tests/Unit/TestNGMailAddressParser.m | 13 +- Tests/Unit/TestNGMimeMessageGenerator.m | 156 ++++++++++------------- Tests/Unit/TestNSString+Utilities.m | 30 ++++- Tests/Unit/TestSBJsonParser.m | 18 ++- Tests/Unit/TestiCalTimeZonePeriod.m | 3 +- Tests/Unit/sogo-tests.m | 87 ++++++++++++- 18 files changed, 439 insertions(+), 210 deletions(-) diff --git a/NEWS b/NEWS index 35720b084..3612f2c2f 100644 --- a/NEWS +++ b/NEWS @@ -2,13 +2,21 @@ ------------------ New features - - Now able to sync only default mail folders when using EAS + - now able to sync only default mail folders when using EAS + +Enhancements + - Unit testing for RTFHandler + - JUnit output for sogo-tests Bug fixes - don't unescape twice mail folder names (#3423) - don't consider mobile Outlook EAS clients as DAV ones (#3431) - we now follow 301 redirects when fetching ICS calendars - when deleting an event using EAS, properly invoke the auto-scheduling code + - do not include failure attachments (really long filenames) + - fix encoding of email subjects with non-ASCII characters + - fix appointment notification mails using SOGoEnableDomainBasedUID configuration + - fix shifts in event times on Outlook 2.3.5 (2016-01-05) ------------------ @@ -18,6 +26,7 @@ Enhancements - return the requested elements on complex requests from Outlook when downloading changes - user sources can be loaded dynamically - unify user sources API + - updated Russian translation (#3383) Bug fixes - properly compute the last week number for the year (#1010) @@ -33,7 +42,6 @@ Bug fixes - accepted & updated event names are now shown correctly in Outlook - provide safe guards in mail and calendar to avoid exceptions while syncing - 2.3.4 (2015-12-15) ------------------ @@ -42,6 +50,7 @@ New features Enhancements - limit the maximum width of toolbar buttons (#3381) + - updated CKEditor to version 4.5.6 Bug fixes - JavaScript exception when printing events from calendars with no assigned color (#3203) diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 5c9d70b39..5079c65cc 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -77,6 +77,17 @@ - (NSArray *) rolesForExchangeRights: (uint32_t) rights { + /* Limitations + + Following rights are not supported by SOGo specifically: + + - DeleteOwned : Delete only own objects + - EditOwned : Edit only own objects + - CreateSubfolders: No calendar subfolders + - FolderOwner: No sharing folder ownership? + - FolderContact: No support to store this information + - FolderVisible: It is inferred by other rights when extracting + */ NSMutableArray *roles; roles = [NSMutableArray arrayWithCapacity: 6]; @@ -121,7 +132,7 @@ if ([roles containsObject: SOGoCalendarRole_PublicModifier] && [roles containsObject: SOGoCalendarRole_PrivateModifier] && [roles containsObject: SOGoCalendarRole_ConfidentialModifier]) - rights |= RightsReadItems | RightsEditAll | RightsEditOwn; + rights |= RightsReadItems | RightsEditAll | RightsEditOwn | RightsFreeBusySimple | RightsFreeBusyDetailed; else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) @@ -135,7 +146,7 @@ if ([roles containsObject: SOGoCalendarRole_ConfidentialDAndTViewer]) rights |= RightsFreeBusyDetailed; - if (rights != 0) + if ((rights & RightsReadItems) != 0 || (rights & RightsCreateItems) != 0 || (rights & RightsDeleteAll) != 0) rights |= RoleNone; /* actually "folder visible" */ // [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights]; diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 9ccec826c..0ec1d59bd 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -64,11 +64,11 @@ static BOOL initialization_done = NO; } \ OC_DEBUG(5, "[SOGo] --->"); -#define NS_CURRENT_THREAD_TRY_UNREGISTER() \ +#define NS_CURRENT_THREAD_TRY_UNREGISTER(rc) \ if (__nsrct_thread_registered) { \ GSUnregisterCurrentThread(); \ } \ - OC_DEBUG(6, "[SOGo] <---"); + OC_DEBUG(6, "[SOGo] <--- [%s]", mapistore_errstr (rc)); #define TRYCATCH_START @try { #define TRYCATCH_END(pool) \ @@ -76,7 +76,7 @@ static BOOL initialization_done = NO; enum mapistore_error ret = sogo_backend_handle_objc_exception(e, __PRETTY_FUNCTION__, __LINE__); \ mapiapp_cleanup(); \ [pool release]; \ - NS_CURRENT_THREAD_TRY_UNREGISTER(); \ + NS_CURRENT_THREAD_TRY_UNREGISTER(ret); \ return ret; \ } \ mapiapp_cleanup(); @@ -253,7 +253,7 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, rc = MAPISTORE_ERROR; [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); return rc; } @@ -291,7 +291,7 @@ sogo_backend_create_root_folder (const char *username, rc = MAPISTORE_ERROR; [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); return rc; } @@ -322,7 +322,7 @@ sogo_backend_list_contexts(const char *username, struct indexing_context *indexi rc = MAPISTORE_ERROR; [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); return rc; } @@ -363,7 +363,7 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -397,7 +397,7 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -439,7 +439,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -480,7 +480,7 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -519,7 +519,7 @@ sogo_folder_delete(void *folder_object) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -549,7 +549,7 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -588,7 +588,7 @@ sogo_folder_open_message(void *folder_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -627,7 +627,7 @@ sogo_folder_create_message(void *folder_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -657,7 +657,7 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -705,7 +705,7 @@ sogo_folder_move_copy_messages(void *folder_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -753,7 +753,7 @@ sogo_folder_move_folder(void *folder_object, void *target_folder_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -795,7 +795,7 @@ sogo_folder_copy_folder(void *folder_object, void *target_folder_object, TALLOC_ TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -831,7 +831,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -869,7 +869,7 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -903,7 +903,7 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -934,7 +934,7 @@ sogo_folder_preload_message_bodies(void *folder_object, enum mapistore_table_typ TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -967,8 +967,8 @@ sogo_message_get_message_data(void *message_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); rc = MAPISTORE_SUCCESS; + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1001,7 +1001,7 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1035,7 +1035,7 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1069,7 +1069,7 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1104,7 +1104,7 @@ sogo_message_modify_recipients (void *message_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1134,7 +1134,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1164,7 +1164,7 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1194,7 +1194,7 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1234,7 +1234,7 @@ sogo_message_attachment_open_embedded_message (void *attachment_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1272,7 +1272,7 @@ sogo_message_attachment_create_embedded_message (void *attachment_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1302,7 +1302,7 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1333,7 +1333,7 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1366,7 +1366,7 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1399,7 +1399,7 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1432,7 +1432,7 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1465,7 +1465,7 @@ sogo_table_get_row_count (void *table_object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1495,8 +1495,8 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); rc = MAPISTORE_SUCCESS; + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1527,7 +1527,7 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1562,7 +1562,7 @@ sogo_properties_get_properties (void *object, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1592,7 +1592,7 @@ sogo_properties_set_properties (void *object, struct SRow *aRow) TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(rc); } else { @@ -1646,7 +1646,7 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx, TRYCATCH_END(pool) [pool release]; - NS_CURRENT_THREAD_TRY_UNREGISTER(); + NS_CURRENT_THREAD_TRY_UNREGISTER(MAPISTORE_SUCCESS); return MAPISTORE_SUCCESS; } diff --git a/OpenChange/RTFHandler.h b/OpenChange/RTFHandler.h index b0ea0b684..ec776a8d3 100644 --- a/OpenChange/RTFHandler.h +++ b/OpenChange/RTFHandler.h @@ -28,6 +28,8 @@ // // // +@class RTFFontTable; + @interface RTFHandler : NSObject { NSMapTable *_charsets; @@ -41,6 +43,11 @@ - (id) initWithData: (NSData *) theData; - (NSMutableData *) parse; + +- (RTFFontTable *) parseFontTable; +- (void) mangleInternalStateWithBytesPtr: (const char*) newBytes + andCurrentPos: (int) newCurrentPos; + @end // @@ -84,6 +91,7 @@ unsigned int index; } +- (NSString *) description; @end // @@ -97,8 +105,8 @@ - (void) addFontInfo: (RTFFontInfo *) theFontInfo atIndex: (unsigned int ) theIndex; - - (RTFFontInfo *) fontInfoAtIndex: (unsigned int ) theIndex; +- (NSString *) description; @end diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index b18754e51..d4f775b5d 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -21,6 +21,7 @@ #include "RTFHandler.h" #include +#include // // Useful macros @@ -352,6 +353,7 @@ const unsigned short ansicpg874[256] = { RTFFontInfo *fontInfo; description = [NSMutableString stringWithFormat: @"Number of fonts: %u\n", [fontInfos count]]; + enumerator = [fontInfos objectEnumerator]; while ((fontInfo = [enumerator nextObject])) { @@ -563,6 +565,9 @@ static void _init_fontCws_table() [super dealloc]; } +/* + Returns pointer to the control word and in len pointer its length including numeric argument +*/ - (const char *) parseControlWord: (unsigned int *) len { const char *start, *end; @@ -1457,4 +1462,12 @@ inline static void parseUl(RTFHandler *self, BOOL hasArg, int arg, RTFFormatting return [_html autorelease]; } +/* This method is for ease of testing and should not be used in normal operations */ +- (void) mangleInternalStateWithBytesPtr: (const char*) newBytes + andCurrentPos: (int) newCurrentPos +{ + _bytes = newBytes; + _current_pos = newCurrentPos; +} + @end diff --git a/SoObjects/Appointments/iCalPerson+SOGo.m b/SoObjects/Appointments/iCalPerson+SOGo.m index 986960902..fc14cf0fd 100644 --- a/SoObjects/Appointments/iCalPerson+SOGo.m +++ b/SoObjects/Appointments/iCalPerson+SOGo.m @@ -23,6 +23,7 @@ #import #import #import +#import #import "iCalPerson+SOGo.h" @@ -64,6 +65,10 @@ static SOGoUserManager *um = nil; return [um getUIDForEmail: [self rfc822Email]]; } +/* + It returns the login if the email of the iCalPerson exists on the + domain of the current active user +*/ - (NSString *) uidInContext: (WOContext *) context { NSString *domain; @@ -73,18 +78,29 @@ static SOGoUserManager *um = nil; return [self uidInDomain: domain]; } +/* + It returns the login if the email of the iCalPerson exists on the + given domain +*/ - (NSString *) uidInDomain: (NSString *) domain { NSDictionary *contact; - NSString *uid; + NSString *uid = nil; if (!um) um = [SOGoUserManager sharedUserManager]; - uid = nil; - contact = [um contactInfosForUserWithUIDorEmail: [self rfc822Email] inDomain: domain]; - if (contact) - uid = [contact valueForKey: @"c_uid"]; + contact = [um contactInfosForUserWithUIDorEmail: [self rfc822Email] + inDomain: domain]; + if (!contact) return nil; + + uid = [contact valueForKey: @"c_uid"]; + + // On multidomain environment without DomainLessLogin enabled the login + // must have the @domain suffix + if ([[SOGoSystemDefaults sharedSystemDefaults] enableDomainBasedUID] + && ![[contact objectForKey: @"DomainLessLogin"] boolValue]) + uid = [NSString stringWithFormat:@"%@@%@", uid, domain]; return uid; } diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 410e8004c..45649e6bc 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import #import @@ -1093,7 +1094,7 @@ static NSString *userAgent = nil; - (NSException *) saveAttachment: (NSData *) _attach withMetadata: (NSDictionary *) metadata { - NSString *p, *name, *mimeType; + NSString *p, *pmime, *name, *mimeType; NSRange r; if (![_attach isNotNull]) { @@ -1125,14 +1126,13 @@ static NSString *userAgent = nil; mimeType = [metadata objectForKey: @"mimetype"]; if ([mimeType length] > 0) { - p = [self pathToAttachmentWithName: - [NSString stringWithFormat: @".%@.mime", name]]; - if (![[mimeType dataUsingEncoding: NSUTF8StringEncoding] - writeToFile: p atomically: YES]) - { - return [NSException exceptionWithHTTPStatus:500 /* Server Error */ - reason: @"Could not write attachment to draft!"]; - } + pmime = [self pathToAttachmentWithName: [NSString stringWithFormat: @".%@.mime", name]]; + if (![[mimeType dataUsingEncoding: NSUTF8StringEncoding] writeToFile: pmime atomically: YES]) + { + [[NSFileManager defaultManager] removeItemAtPath: p error: nil]; + return [NSException exceptionWithHTTPStatus: 500 /* Server Error */ + reason: @"Could not write attachment to draft!"]; + } } return nil; /* everything OK */ diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 6dbcb9689..1dc8bf953 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -492,36 +492,7 @@ static int cssEscapingCount; - (NSString *) asQPSubjectString: (NSString *) encoding { - NSString *qpString, *subjectString; - NSData *subjectData, *destSubjectData; - NSUInteger length, destLength; - unsigned char *destString; - -#warning "encoding" parameter is not useful - subjectData = [self dataUsingEncoding: NSUTF8StringEncoding]; - length = [subjectData length]; - destLength = length * 3; - destString = calloc (destLength, sizeof (char)); - - NGEncodeQuotedPrintableMime ([subjectData bytes], length, - destString, destLength); - - destSubjectData = [NSData dataWithBytesNoCopy: destString - length: strlen ((char *) destString) - freeWhenDone: YES]; - qpString = [[NSString alloc] initWithData: destSubjectData - encoding: NSASCIIStringEncoding]; - [qpString autorelease]; - if ([qpString length] > [self length]) - { - qpString = [qpString stringByReplacingString: @" " withString: @"_"]; - subjectString = [NSString stringWithFormat: @"=?%@?q?%@?=", - encoding, qpString]; - } - else - subjectString = self; - - return subjectString; + return [NGMimeHeaderFieldGenerator encodeQuotedPrintableText: self]; } - (BOOL) caseInsensitiveMatches: (NSString *) match diff --git a/Tests/Unit/GNUmakefile b/Tests/Unit/GNUmakefile index b3b666270..6c93399e7 100644 --- a/Tests/Unit/GNUmakefile +++ b/Tests/Unit/GNUmakefile @@ -21,19 +21,24 @@ $(TEST_TOOL)_OBJC_FILES += \ \ TestSBJsonParser.m \ \ - TestNGMimeAddressHeaderFieldGenerator.m \ + TestNGMimeHeaderFieldGenerator.m \ TestNGMimeMessageGenerator.m \ \ TestNSData+Crypto.m \ TestNSString+Crypto.m \ TestNSString+URLEscaping.m \ TestNSString+Utilities.m \ - TestNGMailAddressParser.m + TestNGMailAddressParser.m \ + \ + TestRTFHandler.m \ + ../../OpenChange/RTFHandler.m + # I don't know how to link against -l:SOGoBackend \ + undefined reference to `__objc_class_name_SOGoMailFolder' TEST_TOOL_NAME = $(TEST_TOOL) $(TEST_TOOL)_CPPFLAGS += \ - -Wall -D_GNU_SOURCE -I../../SOPE/ -I../../SoObjects/ -I../../UI/ + -Wall -D_GNU_SOURCE -I../../SOPE/ -I../../SoObjects/ -I../../UI/ -I../../OpenChange ADDITIONAL_LIB_DIRS += \ -L../../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo -L../../SOPE/NGCards/obj -L../../SOPE/GDLContentStore/obj -lSOGo -lNGMime -lNGCards -lGDLContentStore -lNGExtensions -lSBJson -lobjc \ @@ -47,3 +52,4 @@ include $(GNUSTEP_MAKEFILES)/test-tool.make check :: $(TEST_TOOL) ./obj/sogo-tests + diff --git a/Tests/Unit/SOGoTest.m b/Tests/Unit/SOGoTest.m index 48dafb17c..c5e4dbec1 100644 --- a/Tests/Unit/SOGoTest.m +++ b/Tests/Unit/SOGoTest.m @@ -154,7 +154,8 @@ static NSString *SOGoTestAssertException = @"SOGoTestAssertException"; } NS_ENDHANDLER; - [testRunner incrementTestCounter: failureCode]; + [testRunner incrementTestCounter: failureCode + afterMethod: NSStringFromSelector (testMethod)]; } - (BOOL) run diff --git a/Tests/Unit/SOGoTestRunner.h b/Tests/Unit/SOGoTestRunner.h index 539fb5c33..66ceabdc2 100644 --- a/Tests/Unit/SOGoTestRunner.h +++ b/Tests/Unit/SOGoTestRunner.h @@ -35,20 +35,31 @@ typedef enum { SOGoTestFailureError = 2, } SOGoTestFailureCode; +typedef enum { + SOGoTestTextOutputFormat = 0, + SOGoTestJUnitOutputFormat +} SOGoTestOutputFormat; + @interface SOGoTestRunner : NSObject { - NSMutableArray *messages; + /* An array of arrays whose components are the method name and the + failure message if any */ + NSMutableArray *performedTests; int testCount; int failuresCount; int errorsCount; BOOL hasFailed; + SOGoTestOutputFormat reportFormat; } -+ (SOGoTestRunner *) testRunner; ++ (SOGoTestRunner *) testRunnerWithFormat: (SOGoTestOutputFormat) reportFormat; + +- (void) setReportFormat: (SOGoTestOutputFormat) format; - (int) run; -- (void) incrementTestCounter: (SOGoTestFailureCode) failureCode; +- (void) incrementTestCounter: (SOGoTestFailureCode) failureCode + afterMethod: (NSString *) methodName; - (void) reportException: (NSException *) exception method: (NSString *) methodName withCode: (SOGoTestFailureCode) failureCode; diff --git a/Tests/Unit/SOGoTestRunner.m b/Tests/Unit/SOGoTestRunner.m index 721d3e2d2..677768bbf 100644 --- a/Tests/Unit/SOGoTestRunner.m +++ b/Tests/Unit/SOGoTestRunner.m @@ -22,6 +22,7 @@ #import #import +#import #import #import #import @@ -31,15 +32,16 @@ #import "SOGoTestRunner.h" -#define EXPECTED_FAILURES 3 +#define EXPECTED_FAILURES 0 @implementation SOGoTestRunner -+ (SOGoTestRunner *) testRunner ++ (SOGoTestRunner *) testRunnerWithFormat: (SOGoTestOutputFormat) reportFormat { SOGoTestRunner *testRunner; testRunner = [self new]; + [testRunner setReportFormat: reportFormat]; [testRunner autorelease]; return testRunner; @@ -53,7 +55,8 @@ failuresCount = 0; errorsCount = 0; hasFailed = NO; - messages = [NSMutableArray new]; + performedTests = [NSMutableArray new]; + reportFormat = SOGoTestTextOutputFormat; } return self; @@ -61,10 +64,15 @@ - (void) dealloc { - [messages release]; + [performedTests release]; [super dealloc]; } +- (void) setReportFormat: (SOGoTestOutputFormat) format +{ + reportFormat = format; +} + - (int) run { NSEnumerator *allTestClasses; @@ -95,11 +103,16 @@ } - (void) incrementTestCounter: (SOGoTestFailureCode) failureCode + afterMethod: (NSString *) methodName { static char failureChars[] = { '.', 'F', 'E' }; testCount++; - fprintf (stderr, "%c", failureChars[failureCode]); + if (reportFormat == SOGoTestTextOutputFormat) + fprintf (stderr, "%c", failureChars[failureCode]); + if (failureCode == SOGoTestFailureSuccess) + [performedTests addObject: [NSArray arrayWithObjects: methodName, @"", nil]]; + /* else has been added by reportException method */ } - (void) reportException: (NSException *) exception @@ -134,13 +147,27 @@ errorsCount++; } [message appendString: @"\n"]; - [messages addObject: message]; + + [performedTests addObject: + [NSArray arrayWithObjects: methodName, message, [NSNumber numberWithInt: failureCode], nil]]; } -- (void) displayReport +- (void) displayTextReport { static NSString *separator = @"\n======================================================================\n"; + NSArray *performedTest; + NSMutableArray *messages; NSString *reportMessage; + NSUInteger i, max; + + messages = [NSMutableArray new]; + max = [performedTests count]; + for (i = 0; i < max; i++) + { + performedTest = [performedTests objectAtIndex: i]; + if ([[performedTest objectAtIndex: 1] length] > 0) + [messages addObject: [performedTest objectAtIndex: 1]]; + } if ([messages count]) { @@ -148,6 +175,9 @@ reportMessage = [messages componentsJoinedByString: separator]; fprintf (stderr, "%s", [reportMessage UTF8String]); } + + [messages release]; + fprintf (stderr, "\n----------------------------------------------------------------------\n" "Ran %d tests\n\n", testCount); @@ -158,4 +188,58 @@ fprintf (stderr, "OK\n"); } +- (void) displayJUnitReport +{ + /* Follow JUnit.xsd defined by Apache-Ant project */ + NSArray *performedTest; + NSMutableString *reportMessage; + NSUInteger i, max; + + /* Header */ + reportMessage = [NSMutableString stringWithFormat: @"\n" + @"\n" + @"%@\n", + @"SOGoUnitTests", testCount, errorsCount, failuresCount, + [NSDate date], + @"SOGo and SOPE Unit tests"]; + + /* Test cases */ + max = [performedTests count]; + for (i = 0; i < max; i++) + { + performedTest = [performedTests objectAtIndex: i]; + [reportMessage appendFormat: @"\n", [performedTest objectAtIndex: 0]]; + if ([[performedTest objectAtIndex: 1] length] > 0) + { + if ([performedTest count] > 2 && [[performedTest objectAtIndex: 2] intValue] == SOGoTestFailureFailure) + [reportMessage appendFormat: @"%@\n", [performedTest objectAtIndex: 1]]; + else + [reportMessage appendFormat: @"%@\n", [performedTest objectAtIndex: 1]]; + } + [reportMessage appendString: @"\n"]; + } + + /* End */ + [reportMessage appendString: @""]; + + fprintf (stdout, "%s", [reportMessage UTF8String]); +} + +- (void) displayReport +{ + switch (reportFormat) + { + case SOGoTestTextOutputFormat: + [self displayTextReport]; + break; + ;; + case SOGoTestJUnitOutputFormat: + [self displayJUnitReport]; + break; + ;; + } +} + @end diff --git a/Tests/Unit/TestNGMailAddressParser.m b/Tests/Unit/TestNGMailAddressParser.m index 11c8e05d6..21345a25b 100644 --- a/Tests/Unit/TestNGMailAddressParser.m +++ b/Tests/Unit/TestNGMailAddressParser.m @@ -36,7 +36,7 @@ @"johndown@test.com", // email alone @"", // email between brackets @"\"\" ", // doubled -// @"\"johndown@inverse.ca\" ", // with and without br. + @"\"johndown@inverse.ca\" ", // with and without br. @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla?= ", // accented full name @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla_Bla_Bl=C3=A9?= ", // accented and multiword @"John Down \"Bla Bla\" ", // partly quoted @@ -45,14 +45,13 @@ @"john", // name only, no domain nil ]; NSArray *expectedAddresses = [NSArray arrayWithObjects: - @"johndown@test.com", // email alone - @"johndown@test.com", // email between brackets + @"johndown@test.com", // email alone + @"johndown@test.com", // email between brackets @"johndown@test.com", // doubled -// @"\"johndown@inverse.ca\" ", // with and without br. + @"johndown@test.com", // with and without br. @"johndown@test.com", // accented full name @"johndown@test.com", // accented // and multiword - /* NOTE: the following are wrong but tolerated for now */ @"johndown@test.com", // partly quoted @"johndown@test.com", // full name + email @@ -72,8 +71,8 @@ parsedRecipient = [parser parse]; result = [parsedRecipient address]; error = [NSString - stringWithFormat: @"received '%@' instead of '%@' for '%@'", - result, currentExp, rawAddress]; + stringWithFormat: @"[%d] received '%@' instead of '%@' for '%@'", + count, result, currentExp, rawAddress]; testWithMessage([result isEqualToString: currentExp], error); } } diff --git a/Tests/Unit/TestNGMimeMessageGenerator.m b/Tests/Unit/TestNGMimeMessageGenerator.m index f440bda97..517ca9e89 100644 --- a/Tests/Unit/TestNGMimeMessageGenerator.m +++ b/Tests/Unit/TestNGMimeMessageGenerator.m @@ -19,113 +19,97 @@ #import "SOGoTest.h" #import + @interface TestNGMimeMessageGenerator : SOGoTest @end @implementation TestNGMimeMessageGenerator +/* + This test is actually for SOPE library, not SOGo +*/ - (void) test_generateDataForHeaderField_value { - NGMimeMessageGenerator *generator; NSArray *cases = [NSArray arrayWithObjects: - [NSArray arrayWithObjects: @"Message-ID", @"", @"", nil], - [NSArray arrayWithObjects: @"Content-Type", - @"text/plain; charset=utf-8; format=flowed", - @"text/plain; charset=utf-8; format=flowed", - nil], - [NSArray arrayWithObjects: @"X-FullHeaderOneHebrewOneLatin", - @"עs", - @"=?utf-8?q?=D7=A2s?=", - nil], - [NSArray arrayWithObjects: @"X-FullHeaderOneLatineOneHebrew", - @"sע", - @"=?utf-8?q?s=D7=A2?=", - nil], - [NSArray arrayWithObjects: @"X-FullHeaderOneCharacterHebrew", - @"ע", - @"=?utf-8?q?=D7=A2?=", - nil], - [NSArray arrayWithObjects: @"X-FullHeaderOneCharacterRussian", - @"Б", - @"=?utf-8?q?=D0=91?=", - nil], - - [NSArray arrayWithObjects: @"X-FullHeaderParameter", - @"parameter=ע", - @"parameter==?utf-8?q?=D7=A2?=", - nil], - [NSArray arrayWithObjects: @"X-MixedHeaderParameters", - @"plain; parameter=ע; parameter-plain; parameter2=ea", - @"plain;\n parameter==?utf-8?q?=D7=A2?=;\n parameter-plain; parameter2=ea", - nil], - [NSArray arrayWithObjects: @"X-MixedHeaderAndNoParameter", - @"plain; parameter=ע; parameter-plain; ע", - @"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n =?utf-8?q?=D7=A2?=", - nil], - - [NSArray arrayWithObjects: @"X-MixedHeaderAndTwoParameter", - @"plain; parameter=ע; parameter-plain; z=ע", - @"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n z==?utf-8?q?=D7=A2?=", - nil], - [NSArray arrayWithObjects: @"X-MixedHeaderExtrablanks", - @"plain; parameter=ע; parameter 2spaces; parameter2=ea", - @"plain;\n \\ parameter==?utf-8?q?=D7=A2?=;\n parameter 2spaces; parameter2=ea", - nil], - [NSArray arrayWithObjects: @"X-Encoded-Unbalanced-Paramter-Quote", - @"text/plain; name=\"ע", - @"text/plain;\n name==?utf-8?q?=22=D7=A2?=", - nil], - [NSArray arrayWithObjects: @"content-type", - @"text/plain; name=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"", - @"text/plain;\n name=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"", - nil], - [NSArray arrayWithObjects: @"content-disposition", - @"attachment; filename=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"", - @"attachment;\n filename=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"", - nil], - [NSArray arrayWithObjects: @"content-length", @"2912", @"2912", nil], - [NSArray arrayWithObjects: @"content-transfer-encoding", @"quoted-printable", @"quoted-printable", nil], - nil - ]; - NSEnumerator *enumerator; - NSArray *testCase; + [NSArray arrayWithObjects:@"Message-ID", + @"", + @"", nil], + [NSArray arrayWithObjects:@"Content-Type", + @"text/plain; charset=utf-8; format=flowed", + @"text/plain; charset=utf-8; format=flowed", nil], + [NSArray arrayWithObjects:@"X-FullHeaderOneHebrewOneLatin", + @"עs", + @"=?utf-8?q?=D7=A2s?=", nil], + [NSArray arrayWithObjects:@"X-FullHeaderOneLatineOneHebrew", + @"sע", + @"=?utf-8?q?s=D7=A2?=", nil], + [NSArray arrayWithObjects:@"X-FullHeaderOneCharacterHebrew", + @"ע", + @"=?utf-8?q?=D7=A2?=", nil], + [NSArray arrayWithObjects:@"X-FullHeaderOneCharacterRussian", + @"Б", + @"=?utf-8?q?=D0=91?=", nil], + [NSArray arrayWithObjects:@"X-FullHeaderParameter", + @"parameter=ע", + @"parameter==?utf-8?q?=D7=A2?=", nil], + [NSArray arrayWithObjects:@"X-MixedHeaderParameters", + @"plain; parameter=ע; parameter-plain; parameter2=ea", + @"plain;\n parameter==?utf-8?q?=D7=A2?=;\n parameter-plain; parameter2=ea", nil], + [NSArray arrayWithObjects:@"X-MixedHeaderAndNoParameter", + @"plain; parameter=ע; parameter-plain; ע", + @"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n =?utf-8?q?=D7=A2?=", nil], + [NSArray arrayWithObjects:@"X-MixedHeaderAndTwoParameter", + @"plain; parameter=ע; parameter-plain; z=ע", + @"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n z==?utf-8?q?=D7=A2?=", nil], + [NSArray arrayWithObjects:@"X-MixedHeaderExtrablanks", + @"plain; parameter=ע; parameter 2spaces; parameter2=ea", + @"plain;\n \\ parameter==?utf-8?q?=D7=A2?=;\n parameter 2spaces; parameter2=ea", nil], + [NSArray arrayWithObjects:@"X-Encoded-Unbalanced-Paramter-Quote", + @"text/plain; name=\"ע", + @"text/plain;\n name==?utf-8?q?=22=D7=A2?=", nil], + [NSArray arrayWithObjects:@"content-type", + @"text/plain; name=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"", + @"text/plain;\n name=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0" + @"=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8" + @"=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"", nil], + [NSArray arrayWithObjects:@"content-disposition", + @"attachment; filename=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"", + @"attachment;\n filename=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0" + @"=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8" + @"=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"", nil], + [NSArray arrayWithObjects:@"content-length", @"2912", @"2912", nil], + [NSArray arrayWithObjects:@"content-transfer-encoding", @"quoted-printable", @"quoted-printable", nil], + nil + ]; [NGMimeMessageGenerator initialize]; - generator = [[NGMimeMessageGenerator alloc] init]; + NGMimeMessageGenerator *generator = [[NGMimeMessageGenerator alloc] init]; [generator autorelease]; - enumerator = [cases objectEnumerator]; - while ((testCase = [enumerator nextObject]) != nil) + for (NSArray *testCase in cases) { - NSData *result; - NSMutableData *resultWithNulByte; NSString *header = [testCase objectAtIndex: 0]; NSData *headerData = [testCase objectAtIndex: 1]; NSString *expected = [testCase objectAtIndex: 2]; - result = [generator generateDataForHeaderField: header - value: headerData]; - if (result == nil) - result = [@"[nil]" dataUsingEncoding: NSUTF8StringEncoding]; + NSData *result = [generator generateDataForHeaderField: header + value: headerData]; + if (result == nil) + result = [@"[nil]" dataUsingEncoding: NSUTF8StringEncoding]; - resultWithNulByte = [result mutableCopy]; - [resultWithNulByte appendBytes: "\0" length: 1]; + NSMutableData *resultWithNulByte = [result mutableCopy]; + [resultWithNulByte appendBytes: "\0" length: 1]; NSString *resultString = [NSString stringWithCString:[resultWithNulByte bytes]]; - BOOL testResult = [resultString isEqualToString: expected]; - NSString *diff = [self stringFromDiffBetween: [NSString stringWithString: resultString] - and: [NSString stringWithString: expected]]; - NSString *testErrorMsg = [NSString - stringWithFormat: @">> For %@ header received:\n%@[END]\n>> instead of:\n%@[END]\n>> for:\n%@\n>> diff:\n%@\n>> lengthReceived: %lu lengthExpected: %lu", - header, - resultString, - expected, - headerData, - diff, - [resultString length], - [expected length] - ]; + NSString *diff = [self stringFromDiffBetween: resultString and: expected]; + + NSString *testErrorMsg = [NSString stringWithFormat: + @">> For %@ header received:\n%@[END]\n" + @">> instead of:\n%@[END]\n" + @">> for:\n%@\n>> diff:\n%@\n" + @">> lengthReceived: %lu lengthExpected: %lu", header, resultString, + expected, headerData, diff, [resultString length], [expected length]]; testWithMessage(testResult, testErrorMsg); } diff --git a/Tests/Unit/TestNSString+Utilities.m b/Tests/Unit/TestNSString+Utilities.m index 6442c800f..5dbe0cd9e 100644 --- a/Tests/Unit/TestNSString+Utilities.m +++ b/Tests/Unit/TestNSString+Utilities.m @@ -1,8 +1,10 @@ /* TestNSString+Utilities.m - this file is part of SOGo * * Copyright (C) 2011 Inverse inc + * Copyright (C) 2014 Zentyal * * Author: Wolfgang Sourdeau + * Jesús García Sáez * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +25,7 @@ /* This file is encoded in utf-8. */ #import - +#import #import "SOGoTest.h" @interface TestNSString_plus_Utilities : SOGoTest @@ -52,7 +54,7 @@ NSString *secret = @"this is a secret"; NSString *password = @"qwerty"; NSString *encresult, *decresult; - + encresult = [secret encryptWithKey: nil]; failIf(encresult != nil); encresult = [secret encryptWithKey: @""]; @@ -70,4 +72,28 @@ failIf(![decresult isEqualToString: secret]); } +- (void) test_objectFromJSONString_single_values +{ + NSString *json, *error; + NSInteger expected = 1; + id result; + + // Decode null + json = [NSString stringWithFormat:@"null"]; + result = [json objectFromJSONString]; + testWithMessage(result == [NSNull null], @"Result should be null"); + + // Decode number + json = [NSString stringWithFormat:@"1"]; + result = [json objectFromJSONString]; + error = [NSString stringWithFormat: @"result %@ != expected %d", + result, expected]; + testWithMessage((long)result != (long)expected, error); + + // Decode string + json = [NSString stringWithFormat:@"\"kill me\""]; + result = [json objectFromJSONString]; + testEquals(result, @"kill me"); +} + @end diff --git a/Tests/Unit/TestSBJsonParser.m b/Tests/Unit/TestSBJsonParser.m index e8c8c139b..1d7831cbd 100644 --- a/Tests/Unit/TestSBJsonParser.m +++ b/Tests/Unit/TestSBJsonParser.m @@ -21,6 +21,7 @@ */ #import +#import #import #import @@ -71,6 +72,8 @@ { SBJsonParser *parser; id result; + NSDecimalNumber *obtained, *expected; + NSDictionary *locale; parser = [SBJsonParser new]; [parser autorelease]; @@ -80,17 +83,20 @@ result = [parser objectWithString: @"[ 0 ]"]; testEquals (result, [NSArray arrayWithObject: [NSNumber numberWithInt: 0]]); - + result = [parser objectWithString: @"[ -1 ]"]; testEquals (result, [NSArray arrayWithObject: [NSNumber numberWithInt: -1]]); - + + locale = [NSDictionary dictionaryWithObject: @"." forKey: NSLocaleDecimalSeparator]; result = [parser objectWithString: @"[ 12.3456 ]"]; - testEquals ([result objectAtIndex: 0], - [NSDecimalNumber decimalNumberWithString: @"12.3456"]); + obtained = [result objectAtIndex: 0]; + expected = [NSDecimalNumber decimalNumberWithString: @"12.3456" locale: locale]; + test ([obtained compare: expected] == NSOrderedSame); result = [parser objectWithString: @"[ -312.3456 ]"]; - testEquals (result, [NSArray arrayWithObject: [NSNumber numberWithDouble: -312.3456]]); + obtained = [result objectAtIndex: 0]; + expected = [NSDecimalNumber decimalNumberWithString: @"-312.3456" locale: locale]; + test ([obtained compare: expected] == NSOrderedSame); } @end - diff --git a/Tests/Unit/TestiCalTimeZonePeriod.m b/Tests/Unit/TestiCalTimeZonePeriod.m index b6eb1b1b7..836f0e1ec 100644 --- a/Tests/Unit/TestiCalTimeZonePeriod.m +++ b/Tests/Unit/TestiCalTimeZonePeriod.m @@ -44,6 +44,7 @@ /* TODO: this test fails for obscure reasons, but test__occurrenceForDate_byRRule_ does not, which is a good sign */ + /* - (void) test_occurrenceForDate_ { NSString *periods[] = { (@"BEGIN:DAYLIGHT\r\n" @@ -84,7 +85,7 @@ @" delta = %ld", count, delta])); } } - +*/ - (void) test__occurrenceForDate_byRRule_ { /* all rules are happening on 2010-03-14 */ diff --git a/Tests/Unit/sogo-tests.m b/Tests/Unit/sogo-tests.m index f7189d89e..d30066312 100644 --- a/Tests/Unit/sogo-tests.m +++ b/Tests/Unit/sogo-tests.m @@ -21,15 +21,91 @@ */ #import +#import #import "SOGoTestRunner.h" -int main() +static void Usage () +{ + /* Print usage and exit */ + NSLog (@"sogo-tests [-h|--help] [-f|--format=text|junit]\n" + @" -h, --help\t\t\tdisplay this help information\n" + @" -f, --format=text|junit\treport format. Default: text\n\n"); + exit(0); +} + +static SOGoTestOutputFormat ParseArguments (NSArray *args) +{ + /* Parse arguments from command line */ + BOOL help = NO; + NSString *arg, *format = nil; + NSUInteger i, max; + SOGoTestOutputFormat outFormat; + + max = [args count]; + /* Skip program name */ + i = 1; + while (!help && i < max) + { + arg = [args objectAtIndex: i]; + if ([arg isEqualToString: @"-f"] || [arg isEqualToString: @"--format"]) + { + NSArray *validFormats = [NSArray arrayWithObjects: @"text", @"junit", nil]; + i++; + if (i < max) + { + arg = [args objectAtIndex: i]; + if ([validFormats containsObject: arg]) + format = arg; + else + { + help = YES; + NSLog (@"Invalid format: '%@'. Use 'text' or 'junit'", arg); + } + } + else + { + NSLog (@"Missing format argument"); + help = YES; + } + } + else if ([arg isEqualToString: @"-h"] + || [arg isEqualToString: @"--help"]) + help = YES; + else + { + NSLog (@"Invalid command line argument: '%@'", arg); + help = YES; + } + i++; + } + + + if (help) + { + Usage (); + } + + if (format) + { + if ([format isEqualToString: @"text"]) + outFormat = SOGoTestTextOutputFormat; + else if ([format isEqualToString: @"junit"]) + outFormat = SOGoTestJUnitOutputFormat; + } + else + outFormat = SOGoTestTextOutputFormat; + + return outFormat; +} + +int main(int argc, char *argv[], char *env[]) { NSAutoreleasePool *pool; int rc; NSDictionary *defaults; NSUserDefaults *ud; + SOGoTestOutputFormat reportFormat; pool = [NSAutoreleasePool new]; @@ -42,7 +118,14 @@ int main() forName: @"sogo-tests-volatile"]; [ud addSuiteNamed: @"sogo-tests-volatile"]; - rc = [[SOGoTestRunner testRunner] run]; + /* Process arguments */ + [NSProcessInfo initializeWithArguments: argv + count: argc + environment: env]; + + reportFormat = ParseArguments ([[NSProcessInfo processInfo] arguments]); + + rc = [[SOGoTestRunner testRunnerWithFormat: reportFormat] run]; [pool release]; return rc;