Skip to content

Commit 12db9b7

Browse files
author
Andres Canal
committed
implemented full xep-0030 and added tests
1 parent c4aeead commit 12db9b7

File tree

6 files changed

+341
-7
lines changed

6 files changed

+341
-7
lines changed

Extensions/XEP-0030/XMPPIQ+XEP_0030.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ + (nonnull XMPPIQ *) discoverInfoAssociatedWithJID:(nonnull XMPPJID *)jid {
8080

8181
NSXMLElement *query = [iq elementForName:@"query" xmlns: XMPPDiscoInfoNamespace];
8282
if(query) {
83-
return [query children];
83+
return [query children] ? [query children] : ((NSArray <NSXMLElement *> *)[[NSArray alloc] init]);
8484
}
8585

8686
return nil;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// XMPPServiceDiscovery.h
3+
// Mangosta
4+
//
5+
// Created by Andres Canal on 4/27/16.
6+
// Copyright © 2016 Inaka. All rights reserved.
7+
//
8+
9+
#import <XMPPFramework/XMPPFramework.h>
10+
11+
@class XMPPIDTracker;
12+
13+
@interface XMPPServiceDiscovery : XMPPModule {
14+
XMPPIDTracker *xmppIDTracker;
15+
}
16+
17+
- (void)discoverInformationAboutJID:(nonnull XMPPJID *)jid;
18+
- (void)discoverItemsAssociatedWithJID:(nonnull XMPPJID *)jid;
19+
20+
@end
21+
22+
@protocol XMPPServiceDiscoveryDelegate
23+
24+
@optional
25+
26+
- (void)xmppServiceDiscovery:(nonnull XMPPServiceDiscovery *)sender didDiscoverInformation:(nonnull NSArray<NSXMLElement *> *)items;
27+
- (void)xmppServiceDiscovery:(nonnull XMPPServiceDiscovery *)sender didDiscoverItems:(nonnull NSArray <NSXMLElement *>*)items;
28+
29+
- (void)xmppServiceDiscovery:(nonnull XMPPServiceDiscovery *)sender didFailToDiscover:(nonnull XMPPIQ *)iq;
30+
31+
@end
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// XMPPServiceDiscovery.m
3+
// Mangosta
4+
//
5+
// Created by Andres Canal on 4/27/16.
6+
// Copyright © 2016 Inaka. All rights reserved.
7+
//
8+
9+
#import "XMPPServiceDiscovery.h"
10+
#import "XMPPIQ+XEP_0030.h"
11+
#import "XMPPIDTracker.h"
12+
#import "XMPPConstants.h"
13+
14+
@implementation XMPPServiceDiscovery
15+
16+
- (BOOL)activate:(XMPPStream *)aXmppStream {
17+
18+
if ([super activate:aXmppStream]) {
19+
xmppIDTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];
20+
21+
return YES;
22+
}
23+
24+
return NO;
25+
}
26+
27+
- (void)deactivate {
28+
dispatch_block_t block = ^{ @autoreleasepool {
29+
30+
[xmppIDTracker removeAllIDs];
31+
xmppIDTracker = nil;
32+
33+
}};
34+
35+
if (dispatch_get_specific(moduleQueueTag))
36+
block();
37+
else
38+
dispatch_sync(moduleQueue, block);
39+
40+
[super deactivate];
41+
}
42+
43+
- (void) discoverWithIQ:(XMPPIQ *) infoOrItem {
44+
45+
dispatch_block_t block = ^{ @autoreleasepool {
46+
47+
NSString *iqID = [infoOrItem elementID];
48+
[xmppIDTracker addID:iqID
49+
target:self
50+
selector:@selector(handleDiscovery:withInfo:)
51+
timeout:60.0];
52+
53+
[xmppStream sendElement:infoOrItem];
54+
}};
55+
56+
if (dispatch_get_specific(moduleQueueTag))
57+
block();
58+
else
59+
dispatch_async(moduleQueue, block);
60+
}
61+
62+
- (void)discoverInformationAboutJID:(XMPPJID *)jid{
63+
[self discoverWithIQ:[XMPPIQ discoverInfoAssociatedWithJID:jid]];
64+
}
65+
66+
67+
- (void)discoverItemsAssociatedWithJID:(XMPPJID *)jid{
68+
[self discoverWithIQ:[XMPPIQ discoverItemsAssociatedWithJID:jid]];
69+
}
70+
71+
- (void)handleDiscovery:(XMPPIQ *)iq withInfo:(id <XMPPTrackingInfo>)info{
72+
73+
if ([[iq type] isEqualToString:@"result"]){
74+
NSXMLElement *query = [iq elementForName:@"query"];
75+
NSArray *items = [query children];
76+
77+
if ([query.xmlns isEqualToString:XMPPDiscoInfoNamespace]) {
78+
[multicastDelegate xmppServiceDiscovery:self didDiscoverInformation:items];
79+
} else {
80+
[multicastDelegate xmppServiceDiscovery:self didDiscoverItems:items];
81+
}
82+
83+
} else {
84+
[multicastDelegate xmppServiceDiscovery:self didFailToDiscover:iq];
85+
}
86+
}
87+
88+
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq {
89+
90+
NSString *type = [iq type];
91+
92+
if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"]){
93+
return [xmppIDTracker invokeForID:[iq elementID] withObject:iq];
94+
}
95+
96+
return NO;
97+
}
98+
99+
@end
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
//
2+
// XMPPServiceDiscoveryTests.m
3+
// XMPPFrameworkTests
4+
//
5+
// Created by Andres Canal on 5/27/16.
6+
//
7+
//
8+
9+
#import <XCTest/XCTest.h>
10+
#import "XMPPFramework/XMPPServiceDiscovery.h"
11+
#import "XMPPMockStream.h"
12+
13+
@interface XMPPServiceDiscoveryTests : XCTestCase <XMPPServiceDiscoveryDelegate>
14+
@property (nonatomic, strong) XCTestExpectation *delegateExpectation;
15+
@end
16+
17+
@implementation XMPPServiceDiscoveryTests
18+
19+
- (void) testDiscoverInformationAbout{
20+
self.delegateExpectation = [self expectationWithDescription:@"Information Response"];
21+
22+
XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
23+
XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init];
24+
[serviceDiscovery activate:streamTest];
25+
[serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()];
26+
27+
__weak typeof(XMPPMockStream) *weakStreamTest = streamTest;
28+
streamTest.elementReceived = ^void(NSXMLElement *element) {
29+
NSString *elementID = [element attributeForName:@"id"].stringValue;
30+
XMPPIQ *iq = [self fakeInfoIQWithID:elementID];
31+
[weakStreamTest fakeIQResponse:iq];
32+
};
33+
34+
[serviceDiscovery discoverInformationAboutJID:[XMPPJID jidWithString:@"test.com"]];
35+
36+
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
37+
if(error){
38+
XCTFail(@"Expectation Failed with error: %@", error);
39+
}
40+
}];
41+
}
42+
43+
- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverInformation:(NSArray *)items{
44+
XCTAssertEqual(items.count, 9);
45+
[self.delegateExpectation fulfill];
46+
}
47+
48+
- (void) testDiscoverItems{
49+
self.delegateExpectation = [self expectationWithDescription:@"Items Response"];
50+
51+
XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
52+
XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init];
53+
[serviceDiscovery activate:streamTest];
54+
[serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()];
55+
56+
__weak typeof(XMPPMockStream) *weakStreamTest = streamTest;
57+
streamTest.elementReceived = ^void(NSXMLElement *element) {
58+
NSString *elementID = [element attributeForName:@"id"].stringValue;
59+
XMPPIQ *iq = [self fakeItemIQWithID:elementID];
60+
[weakStreamTest fakeIQResponse:iq];
61+
};
62+
63+
[serviceDiscovery discoverItemsAssociatedWithJID:[XMPPJID jidWithString:@"test.com"]];
64+
65+
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
66+
if(error){
67+
XCTFail(@"Expectation Failed with error: %@", error);
68+
}
69+
}];
70+
}
71+
72+
- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverItems:(NSArray *)items{
73+
XCTAssertEqual(items.count, 8);
74+
[self.delegateExpectation fulfill];
75+
}
76+
77+
- (void) testError{
78+
self.delegateExpectation = [self expectationWithDescription:@"Error Response"];
79+
80+
XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
81+
XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init];
82+
[serviceDiscovery activate:streamTest];
83+
[serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()];
84+
85+
__weak typeof(XMPPMockStream) *weakStreamTest = streamTest;
86+
streamTest.elementReceived = ^void(NSXMLElement *element) {
87+
NSString *elementID = [element attributeForName:@"id"].stringValue;
88+
XMPPIQ *iq = [self fakeErrorWithID:elementID];
89+
[weakStreamTest fakeIQResponse:iq];
90+
};
91+
92+
[serviceDiscovery discoverItemsAssociatedWithJID:[XMPPJID jidWithString:@"test.com"]];
93+
94+
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
95+
if(error){
96+
XCTFail(@"Expectation Failed with error: %@", error);
97+
}
98+
}];
99+
}
100+
101+
- (XMPPIQ *)fakeErrorWithID:(NSString *) elementID{
102+
NSMutableString *s = [NSMutableString string];
103+
[s appendString:@"<iq type='error'"];
104+
[s appendString:@" from='mim.shakespeare.lit'"];
105+
[s appendString:@" to='[email protected]/orchard'"];
106+
[s appendString:@" id='info3'>"];
107+
[s appendString:@" <query xmlns='http://jabber.org/protocol/disco#info' "];
108+
[s appendString:@" node='http://jabber.org/protocol/commands'/>"];
109+
[s appendString:@" <error type='cancel'>"];
110+
[s appendString:@" <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"];
111+
[s appendString:@" </error>"];
112+
[s appendString:@"</iq>"];
113+
114+
NSError *error;
115+
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error];
116+
XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]];
117+
[iq addAttributeWithName:@"id" stringValue:elementID];
118+
119+
return iq;
120+
}
121+
122+
- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didFailToDiscover:(XMPPIQ *)iq{
123+
XCTAssertNotNil([iq elementForName:@"error"]);
124+
[self.delegateExpectation fulfill];
125+
}
126+
127+
128+
- (XMPPIQ *)fakeItemIQWithID:(NSString *) elementID{
129+
NSMutableString *s = [NSMutableString string];
130+
[s appendString:@"<iq type='result'"];
131+
[s appendString:@" from='shakespeare.lit'"];
132+
[s appendString:@" to='[email protected]/orchard'"];
133+
[s appendString:@" id='items1'>"];
134+
[s appendString:@" <query xmlns='http://jabber.org/protocol/disco#items'>"];
135+
[s appendString:@" <item jid='people.shakespeare.lit'"];
136+
[s appendString:@" name='Directory of Characters'/>"];
137+
[s appendString:@" <item jid='plays.shakespeare.lit'"];
138+
[s appendString:@" name='Play-Specific Chatrooms'/>"];
139+
[s appendString:@" <item jid='mim.shakespeare.lit'"];
140+
[s appendString:@" name='Gateway to Marlowe IM'/>"];
141+
[s appendString:@" <item jid='words.shakespeare.lit'"];
142+
[s appendString:@" name='Shakespearean Lexicon'/>"];
143+
[s appendString:@" <item jid='globe.shakespeare.lit'"];
144+
[s appendString:@" name='Calendar of Performances'/>"];
145+
[s appendString:@" <item jid='headlines.shakespeare.lit'"];
146+
[s appendString:@" name='Latest Shakespearean News'/>"];
147+
[s appendString:@" <item jid='catalog.shakespeare.lit'"];
148+
[s appendString:@" name='Buy Shakespeare Stuff!'/>"];
149+
[s appendString:@" <item jid='en2fr.shakespeare.lit'"];
150+
[s appendString:@" name='French Translation Service'/>"];
151+
[s appendString:@" </query>"];
152+
[s appendString:@"</iq>"];
153+
154+
NSError *error;
155+
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error];
156+
XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]];
157+
[iq addAttributeWithName:@"id" stringValue:elementID];
158+
159+
return iq;
160+
}
161+
162+
- (XMPPIQ *)fakeInfoIQWithID:(NSString *) elementID{
163+
164+
NSMutableString *s = [NSMutableString string];
165+
[s appendString: @"<iq type='result'"];
166+
[s appendString: @" from='plays.shakespeare.lit'"];
167+
[s appendString: @" to='[email protected]/orchard'"];
168+
[s appendString: @" id='info1'>"];
169+
[s appendString: @" <query xmlns='http://jabber.org/protocol/disco#info'>"];
170+
[s appendString: @" <identity"];
171+
[s appendString: @" category='conference'"];
172+
[s appendString: @" type='text'"];
173+
[s appendString: @" name='Play-Specific Chatrooms'/>"];
174+
[s appendString: @" <identity"];
175+
[s appendString: @" category='directory'"];
176+
[s appendString: @" type='chatroom'"];
177+
[s appendString: @" name='Play-Specific Chatrooms'/>"];
178+
[s appendString: @" <feature var='http://jabber.org/protocol/disco#info'/>"];
179+
[s appendString: @" <feature var='http://jabber.org/protocol/disco#items'/>"];
180+
[s appendString: @" <feature var='http://jabber.org/protocol/muc'/>"];
181+
[s appendString: @" <feature var='jabber:iq:register'/>"];
182+
[s appendString: @" <feature var='jabber:iq:search'/>"];
183+
[s appendString: @" <feature var='jabber:iq:time'/>"];
184+
[s appendString: @" <feature var='jabber:iq:version'/>"];
185+
[s appendString: @" </query>"];
186+
[s appendString: @"</iq>"];
187+
188+
NSError *error;
189+
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error];
190+
XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]];
191+
[iq addAttributeWithName:@"id" stringValue:elementID];
192+
193+
return iq;
194+
}
195+
196+
@end

Xcode/Testing-iOS/XMPPFrameworkTests.xcodeproj/project.pbxproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
D973A0871D2F18040096F3ED /* XMPPvCardTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D973A07B1D2F18040096F3ED /* XMPPvCardTests.m */; };
2222
D973A0891D2F18310096F3ED /* XMPPSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = D973A0881D2F18310096F3ED /* XMPPSwift.swift */; };
2323
E0B1B8161D33F5A700B7E608 /* XMPP0030Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B1B8141D33F5A700B7E608 /* XMPP0030Tests.m */; };
24+
E0B3E9211D34083800EAD41B /* XMPPServiceDiscoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B3E9201D34083800EAD41B /* XMPPServiceDiscoveryTests.m */; };
2425
FDD2AB232C05507F2045FFFC /* Pods_XMPPFrameworkTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD0B17267211A912DE2098E /* Pods_XMPPFrameworkTests.framework */; };
2526
/* End PBXBuildFile section */
2627

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

5153
/* Begin PBXFrameworksBuildPhase section */
@@ -79,23 +81,24 @@
7981
63F50D941C60208200CA0201 /* XMPPFrameworkTests */ = {
8082
isa = PBXGroup;
8183
children = (
84+
63F50D971C60208200CA0201 /* Info.plist */,
85+
D973A06E1D2F18030096F3ED /* XMPPFrameworkTests-Bridging-Header.h */,
8286
D973A0881D2F18310096F3ED /* XMPPSwift.swift */,
87+
D973A0761D2F18040096F3ED /* XMPPPushTests.swift */,
8388
D973A06F1D2F18040096F3ED /* CapabilitiesHashingTest.m */,
8489
D973A0701D2F18040096F3ED /* EncodeDecodeTest.m */,
8590
D973A0711D2F18040096F3ED /* XMPPHTTPFileUploadTests.m */,
8691
D973A0721D2F18040096F3ED /* XMPPMessageArchiveManagementTests.m */,
8792
D973A0731D2F18040096F3ED /* XMPPMockStream.h */,
8893
D973A0741D2F18040096F3ED /* XMPPMockStream.m */,
8994
D973A0751D2F18040096F3ED /* XMPPMUCLightTests.m */,
90-
D973A0761D2F18040096F3ED /* XMPPPushTests.swift */,
9195
D973A0771D2F18040096F3ED /* XMPPRoomLightCoreDataStorageTests.m */,
9296
D973A0781D2F18040096F3ED /* XMPPRoomLightTests.m */,
9397
D973A0791D2F18040096F3ED /* XMPPStorageHintTests.m */,
9498
D973A07A1D2F18040096F3ED /* XMPPURITests.m */,
9599
D973A07B1D2F18040096F3ED /* XMPPvCardTests.m */,
96100
E0B1B8141D33F5A700B7E608 /* XMPP0030Tests.m */,
97-
63F50D971C60208200CA0201 /* Info.plist */,
98-
D973A06E1D2F18030096F3ED /* XMPPFrameworkTests-Bridging-Header.h */,
101+
E0B3E9201D34083800EAD41B /* XMPPServiceDiscoveryTests.m */,
99102
);
100103
path = XMPPFrameworkTests;
101104
sourceTree = "<group>";
@@ -262,6 +265,7 @@
262265
D973A07F1D2F18040096F3ED /* XMPPMessageArchiveManagementTests.m in Sources */,
263266
D973A07E1D2F18040096F3ED /* XMPPHTTPFileUploadTests.m in Sources */,
264267
D973A0821D2F18040096F3ED /* XMPPPushTests.swift in Sources */,
268+
E0B3E9211D34083800EAD41B /* XMPPServiceDiscoveryTests.m in Sources */,
265269
D973A0851D2F18040096F3ED /* XMPPStorageHintTests.m in Sources */,
266270
D973A0891D2F18310096F3ED /* XMPPSwift.swift in Sources */,
267271
D973A0871D2F18040096F3ED /* XMPPvCardTests.m in Sources */,

0 commit comments

Comments
 (0)