Skip to content

Commit

Permalink
implemented full xep-0030 and added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Canal committed Jul 11, 2016
1 parent c4aeead commit 12db9b7
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Extensions/XEP-0030/XMPPIQ+XEP_0030.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ + (nonnull XMPPIQ *) discoverInfoAssociatedWithJID:(nonnull XMPPJID *)jid {

NSXMLElement *query = [iq elementForName:@"query" xmlns: XMPPDiscoInfoNamespace];
if(query) {
return [query children];
return [query children] ? [query children] : ((NSArray <NSXMLElement *> *)[[NSArray alloc] init]);
}

return nil;
Expand Down
31 changes: 31 additions & 0 deletions Extensions/XEP-0030/XMPPServiceDiscovery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// XMPPServiceDiscovery.h
// Mangosta
//
// Created by Andres Canal on 4/27/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import <XMPPFramework/XMPPFramework.h>

@class XMPPIDTracker;

@interface XMPPServiceDiscovery : XMPPModule {
XMPPIDTracker *xmppIDTracker;
}

- (void)discoverInformationAboutJID:(nonnull XMPPJID *)jid;
- (void)discoverItemsAssociatedWithJID:(nonnull XMPPJID *)jid;

@end

@protocol XMPPServiceDiscoveryDelegate

@optional

- (void)xmppServiceDiscovery:(nonnull XMPPServiceDiscovery *)sender didDiscoverInformation:(nonnull NSArray<NSXMLElement *> *)items;
- (void)xmppServiceDiscovery:(nonnull XMPPServiceDiscovery *)sender didDiscoverItems:(nonnull NSArray <NSXMLElement *>*)items;

- (void)xmppServiceDiscovery:(nonnull XMPPServiceDiscovery *)sender didFailToDiscover:(nonnull XMPPIQ *)iq;

@end
99 changes: 99 additions & 0 deletions Extensions/XEP-0030/XMPPServiceDiscovery.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// XMPPServiceDiscovery.m
// Mangosta
//
// Created by Andres Canal on 4/27/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import "XMPPServiceDiscovery.h"
#import "XMPPIQ+XEP_0030.h"
#import "XMPPIDTracker.h"
#import "XMPPConstants.h"

@implementation XMPPServiceDiscovery

- (BOOL)activate:(XMPPStream *)aXmppStream {

if ([super activate:aXmppStream]) {
xmppIDTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];

return YES;
}

return NO;
}

- (void)deactivate {
dispatch_block_t block = ^{ @autoreleasepool {

[xmppIDTracker removeAllIDs];
xmppIDTracker = nil;

}};

if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_sync(moduleQueue, block);

[super deactivate];
}

- (void) discoverWithIQ:(XMPPIQ *) infoOrItem {

dispatch_block_t block = ^{ @autoreleasepool {

NSString *iqID = [infoOrItem elementID];
[xmppIDTracker addID:iqID
target:self
selector:@selector(handleDiscovery:withInfo:)
timeout:60.0];

[xmppStream sendElement:infoOrItem];
}};

if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_async(moduleQueue, block);
}

- (void)discoverInformationAboutJID:(XMPPJID *)jid{
[self discoverWithIQ:[XMPPIQ discoverInfoAssociatedWithJID:jid]];
}


- (void)discoverItemsAssociatedWithJID:(XMPPJID *)jid{
[self discoverWithIQ:[XMPPIQ discoverItemsAssociatedWithJID:jid]];
}

- (void)handleDiscovery:(XMPPIQ *)iq withInfo:(id <XMPPTrackingInfo>)info{

if ([[iq type] isEqualToString:@"result"]){
NSXMLElement *query = [iq elementForName:@"query"];
NSArray *items = [query children];

if ([query.xmlns isEqualToString:XMPPDiscoInfoNamespace]) {
[multicastDelegate xmppServiceDiscovery:self didDiscoverInformation:items];
} else {
[multicastDelegate xmppServiceDiscovery:self didDiscoverItems:items];
}

} else {
[multicastDelegate xmppServiceDiscovery:self didFailToDiscover:iq];
}
}

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq {

NSString *type = [iq type];

if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"]){
return [xmppIDTracker invokeForID:[iq elementID] withObject:iq];
}

return NO;
}

@end
196 changes: 196 additions & 0 deletions Xcode/Testing-Shared/XMPPServiceDiscoveryTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
//
// XMPPServiceDiscoveryTests.m
// XMPPFrameworkTests
//
// Created by Andres Canal on 5/27/16.
//
//

#import <XCTest/XCTest.h>
#import "XMPPFramework/XMPPServiceDiscovery.h"
#import "XMPPMockStream.h"

@interface XMPPServiceDiscoveryTests : XCTestCase <XMPPServiceDiscoveryDelegate>
@property (nonatomic, strong) XCTestExpectation *delegateExpectation;
@end

@implementation XMPPServiceDiscoveryTests

- (void) testDiscoverInformationAbout{
self.delegateExpectation = [self expectationWithDescription:@"Information Response"];

XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init];
[serviceDiscovery activate:streamTest];
[serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()];

__weak typeof(XMPPMockStream) *weakStreamTest = streamTest;
streamTest.elementReceived = ^void(NSXMLElement *element) {
NSString *elementID = [element attributeForName:@"id"].stringValue;
XMPPIQ *iq = [self fakeInfoIQWithID:elementID];
[weakStreamTest fakeIQResponse:iq];
};

[serviceDiscovery discoverInformationAboutJID:[XMPPJID jidWithString:@"test.com"]];

[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
if(error){
XCTFail(@"Expectation Failed with error: %@", error);
}
}];
}

- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverInformation:(NSArray *)items{
XCTAssertEqual(items.count, 9);
[self.delegateExpectation fulfill];
}

- (void) testDiscoverItems{
self.delegateExpectation = [self expectationWithDescription:@"Items Response"];

XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init];
[serviceDiscovery activate:streamTest];
[serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()];

__weak typeof(XMPPMockStream) *weakStreamTest = streamTest;
streamTest.elementReceived = ^void(NSXMLElement *element) {
NSString *elementID = [element attributeForName:@"id"].stringValue;
XMPPIQ *iq = [self fakeItemIQWithID:elementID];
[weakStreamTest fakeIQResponse:iq];
};

[serviceDiscovery discoverItemsAssociatedWithJID:[XMPPJID jidWithString:@"test.com"]];

[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
if(error){
XCTFail(@"Expectation Failed with error: %@", error);
}
}];
}

- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverItems:(NSArray *)items{
XCTAssertEqual(items.count, 8);
[self.delegateExpectation fulfill];
}

- (void) testError{
self.delegateExpectation = [self expectationWithDescription:@"Error Response"];

XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init];
[serviceDiscovery activate:streamTest];
[serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()];

__weak typeof(XMPPMockStream) *weakStreamTest = streamTest;
streamTest.elementReceived = ^void(NSXMLElement *element) {
NSString *elementID = [element attributeForName:@"id"].stringValue;
XMPPIQ *iq = [self fakeErrorWithID:elementID];
[weakStreamTest fakeIQResponse:iq];
};

[serviceDiscovery discoverItemsAssociatedWithJID:[XMPPJID jidWithString:@"test.com"]];

[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
if(error){
XCTFail(@"Expectation Failed with error: %@", error);
}
}];
}

- (XMPPIQ *)fakeErrorWithID:(NSString *) elementID{
NSMutableString *s = [NSMutableString string];
[s appendString:@"<iq type='error'"];
[s appendString:@" from='mim.shakespeare.lit'"];
[s appendString:@" to='romeo@montague.net/orchard'"];
[s appendString:@" id='info3'>"];
[s appendString:@" <query xmlns='http://jabber.org/protocol/disco#info' "];
[s appendString:@" node='http://jabber.org/protocol/commands'/>"];
[s appendString:@" <error type='cancel'>"];
[s appendString:@" <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"];
[s appendString:@" </error>"];
[s appendString:@"</iq>"];

NSError *error;
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error];
XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]];
[iq addAttributeWithName:@"id" stringValue:elementID];

return iq;
}

- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didFailToDiscover:(XMPPIQ *)iq{
XCTAssertNotNil([iq elementForName:@"error"]);
[self.delegateExpectation fulfill];
}


- (XMPPIQ *)fakeItemIQWithID:(NSString *) elementID{
NSMutableString *s = [NSMutableString string];
[s appendString:@"<iq type='result'"];
[s appendString:@" from='shakespeare.lit'"];
[s appendString:@" to='romeo@montague.net/orchard'"];
[s appendString:@" id='items1'>"];
[s appendString:@" <query xmlns='http://jabber.org/protocol/disco#items'>"];
[s appendString:@" <item jid='people.shakespeare.lit'"];
[s appendString:@" name='Directory of Characters'/>"];
[s appendString:@" <item jid='plays.shakespeare.lit'"];
[s appendString:@" name='Play-Specific Chatrooms'/>"];
[s appendString:@" <item jid='mim.shakespeare.lit'"];
[s appendString:@" name='Gateway to Marlowe IM'/>"];
[s appendString:@" <item jid='words.shakespeare.lit'"];
[s appendString:@" name='Shakespearean Lexicon'/>"];
[s appendString:@" <item jid='globe.shakespeare.lit'"];
[s appendString:@" name='Calendar of Performances'/>"];
[s appendString:@" <item jid='headlines.shakespeare.lit'"];
[s appendString:@" name='Latest Shakespearean News'/>"];
[s appendString:@" <item jid='catalog.shakespeare.lit'"];
[s appendString:@" name='Buy Shakespeare Stuff!'/>"];
[s appendString:@" <item jid='en2fr.shakespeare.lit'"];
[s appendString:@" name='French Translation Service'/>"];
[s appendString:@" </query>"];
[s appendString:@"</iq>"];

NSError *error;
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error];
XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]];
[iq addAttributeWithName:@"id" stringValue:elementID];

return iq;
}

- (XMPPIQ *)fakeInfoIQWithID:(NSString *) elementID{

NSMutableString *s = [NSMutableString string];
[s appendString: @"<iq type='result'"];
[s appendString: @" from='plays.shakespeare.lit'"];
[s appendString: @" to='romeo@montague.net/orchard'"];
[s appendString: @" id='info1'>"];
[s appendString: @" <query xmlns='http://jabber.org/protocol/disco#info'>"];
[s appendString: @" <identity"];
[s appendString: @" category='conference'"];
[s appendString: @" type='text'"];
[s appendString: @" name='Play-Specific Chatrooms'/>"];
[s appendString: @" <identity"];
[s appendString: @" category='directory'"];
[s appendString: @" type='chatroom'"];
[s appendString: @" name='Play-Specific Chatrooms'/>"];
[s appendString: @" <feature var='http://jabber.org/protocol/disco#info'/>"];
[s appendString: @" <feature var='http://jabber.org/protocol/disco#items'/>"];
[s appendString: @" <feature var='http://jabber.org/protocol/muc'/>"];
[s appendString: @" <feature var='jabber:iq:register'/>"];
[s appendString: @" <feature var='jabber:iq:search'/>"];
[s appendString: @" <feature var='jabber:iq:time'/>"];
[s appendString: @" <feature var='jabber:iq:version'/>"];
[s appendString: @" </query>"];
[s appendString: @"</iq>"];

NSError *error;
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error];
XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]];
[iq addAttributeWithName:@"id" stringValue:elementID];

return iq;
}

@end
10 changes: 7 additions & 3 deletions Xcode/Testing-iOS/XMPPFrameworkTests.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
D973A0871D2F18040096F3ED /* XMPPvCardTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D973A07B1D2F18040096F3ED /* XMPPvCardTests.m */; };
D973A0891D2F18310096F3ED /* XMPPSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = D973A0881D2F18310096F3ED /* XMPPSwift.swift */; };
E0B1B8161D33F5A700B7E608 /* XMPP0030Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B1B8141D33F5A700B7E608 /* XMPP0030Tests.m */; };
E0B3E9211D34083800EAD41B /* XMPPServiceDiscoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B3E9201D34083800EAD41B /* XMPPServiceDiscoveryTests.m */; };
FDD2AB232C05507F2045FFFC /* Pods_XMPPFrameworkTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD0B17267211A912DE2098E /* Pods_XMPPFrameworkTests.framework */; };
/* End PBXBuildFile section */

Expand All @@ -46,6 +47,7 @@
D973A07B1D2F18040096F3ED /* XMPPvCardTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPvCardTests.m; path = "../../Testing-Shared/XMPPvCardTests.m"; sourceTree = "<group>"; };
D973A0881D2F18310096F3ED /* XMPPSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XMPPSwift.swift; path = "../../Testing-Shared/XMPPSwift.swift"; sourceTree = "<group>"; };
E0B1B8141D33F5A700B7E608 /* XMPP0030Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPP0030Tests.m; path = "../../Testing-Shared/XMPP0030Tests.m"; sourceTree = "<group>"; };
E0B3E9201D34083800EAD41B /* XMPPServiceDiscoveryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPServiceDiscoveryTests.m; path = "../../Testing-Shared/XMPPServiceDiscoveryTests.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -79,23 +81,24 @@
63F50D941C60208200CA0201 /* XMPPFrameworkTests */ = {
isa = PBXGroup;
children = (
63F50D971C60208200CA0201 /* Info.plist */,
D973A06E1D2F18030096F3ED /* XMPPFrameworkTests-Bridging-Header.h */,
D973A0881D2F18310096F3ED /* XMPPSwift.swift */,
D973A0761D2F18040096F3ED /* XMPPPushTests.swift */,
D973A06F1D2F18040096F3ED /* CapabilitiesHashingTest.m */,
D973A0701D2F18040096F3ED /* EncodeDecodeTest.m */,
D973A0711D2F18040096F3ED /* XMPPHTTPFileUploadTests.m */,
D973A0721D2F18040096F3ED /* XMPPMessageArchiveManagementTests.m */,
D973A0731D2F18040096F3ED /* XMPPMockStream.h */,
D973A0741D2F18040096F3ED /* XMPPMockStream.m */,
D973A0751D2F18040096F3ED /* XMPPMUCLightTests.m */,
D973A0761D2F18040096F3ED /* XMPPPushTests.swift */,
D973A0771D2F18040096F3ED /* XMPPRoomLightCoreDataStorageTests.m */,
D973A0781D2F18040096F3ED /* XMPPRoomLightTests.m */,
D973A0791D2F18040096F3ED /* XMPPStorageHintTests.m */,
D973A07A1D2F18040096F3ED /* XMPPURITests.m */,
D973A07B1D2F18040096F3ED /* XMPPvCardTests.m */,
E0B1B8141D33F5A700B7E608 /* XMPP0030Tests.m */,
63F50D971C60208200CA0201 /* Info.plist */,
D973A06E1D2F18030096F3ED /* XMPPFrameworkTests-Bridging-Header.h */,
E0B3E9201D34083800EAD41B /* XMPPServiceDiscoveryTests.m */,
);
path = XMPPFrameworkTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -262,6 +265,7 @@
D973A07F1D2F18040096F3ED /* XMPPMessageArchiveManagementTests.m in Sources */,
D973A07E1D2F18040096F3ED /* XMPPHTTPFileUploadTests.m in Sources */,
D973A0821D2F18040096F3ED /* XMPPPushTests.swift in Sources */,
E0B3E9211D34083800EAD41B /* XMPPServiceDiscoveryTests.m in Sources */,
D973A0851D2F18040096F3ED /* XMPPStorageHintTests.m in Sources */,
D973A0891D2F18310096F3ED /* XMPPSwift.swift in Sources */,
D973A0871D2F18040096F3ED /* XMPPvCardTests.m in Sources */,
Expand Down
Loading

0 comments on commit 12db9b7

Please sign in to comment.