ShaarliStateParseTest.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. //
  2. // ShaarliStateParseTest.m
  3. // ShaarliOS
  4. //
  5. // Created by Marcus Rohrmoser on 02.08.15.
  6. // Copyright (c) 2015-2016 Marcus Rohrmoser http://mro.name/me. All rights reserved.
  7. //
  8. // This program is free software: you can redistribute it and/or modify
  9. // it under the terms of the GNU General Public License as published by
  10. // the Free Software Foundation, either version 3 of the License, or
  11. // (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. //
  21. #import <UIKit/UIKit.h>
  22. #import "XCTestCase+Tools.h"
  23. #import "MROStateMachine.h"
  24. #pragma mark The Parser
  25. #define M_FORM @"form"
  26. #define F_TOKEN F_K_TOKEN
  27. #define M_HAS_LOGOUT @"has_logout"
  28. #define T_A @"a"
  29. #define T_DIV @"div"
  30. #define T_SPAN @"span"
  31. #define M_TITLE @"title"
  32. #define M_TEXT @"text"
  33. #define M_TAGS @"tags"
  34. #define M_ID_HEADERFORM @"headerform"
  35. #define M_ID_CLOUDTAG @"cloudtag"
  36. #define M_TAG_COUNT @"tag count"
  37. #define M_TAG_HREF @"tag href"
  38. #define M_TAG_LABEL @"tag label"
  39. #define M_STATE_MACHINE @"statemachine"
  40. #import <libxml2/libxml/HTMLparser.h>
  41. static void ShaarliHtml_StartElement(void *voidContext, const xmlChar *name, const xmlChar **attributes)
  42. {
  43. assert(voidContext && "ouch");
  44. NSMutableDictionary *d = (__bridge NSMutableDictionary *)voidContext;
  45. MROStateMachine *sm = d[M_STATE_MACHINE];
  46. if( [sm isCurrentStateName:@"response"] && 0 == strcmp("div", (const char *)name) && 0 == strcmp("id", (const char *)attributes[0]) && 0 == strcmp("headerform", (const char *)attributes[1]) ) {
  47. [sm sendAction:@selector(transitionForm:)];
  48. }
  49. }
  50. static void ShaarliTextRefill(NSMutableDictionary *d, NSString *mark, NSString *tag)
  51. {
  52. NSMutableData *dat = d[M_TEXT];
  53. NSString *str = [[NSString alloc] initWithData:dat encoding:NSUTF8StringEncoding];
  54. d[mark] = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  55. [d removeObjectForKey:tag];
  56. [dat setData:nil];
  57. }
  58. static void ShaarliHtml_EndElement(void *voidContext, const xmlChar *name)
  59. {
  60. assert(voidContext && "ouch");
  61. NSMutableDictionary *d = (__bridge NSMutableDictionary *)voidContext;
  62. }
  63. static void ShaarliHtml_Characters(void *voidContext, const xmlChar *ch, int len)
  64. {
  65. assert(voidContext && "ouch");
  66. NSMutableDictionary *d = (__bridge NSMutableDictionary *)voidContext;
  67. }
  68. static htmlSAXHandler FormField_Handler = {
  69. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ShaarliHtml_StartElement, ShaarliHtml_EndElement,
  70. NULL, ShaarliHtml_Characters,
  71. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // cdata,
  72. NULL, 0, NULL, NULL, NULL, NULL
  73. };
  74. #pragma mark -
  75. @interface ShaarliStateParseTest : XCTestCase
  76. @end
  77. @implementation ShaarliStateParseTest
  78. #pragma mark State Machine Actions
  79. /** parse form data (token!) from the HTML response: */
  80. -(NSDictionary *)parseShaarliHtml:(NSData *)data error:(NSError **)error
  81. {
  82. NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:4];
  83. MROStateMachine *sm = [[MROStateMachine alloc] initWithTarget:self name:@"Shaarli Html Responses"];
  84. [sm addTransitionFrom:@"response" to:@"title" guard:nil];
  85. [sm addTransitionFrom:@"title" to:@"title"];
  86. [sm addTransitionFrom:@"title" to:@"response"];
  87. [sm addTransitionFrom:@"response" to:@"form"];
  88. [sm addTransitionFrom:@"form" to:@"field"];
  89. [sm addTransitionFrom:@"field" to:@"form"];
  90. [sm addTransitionFrom:@"form" to:@"response"];
  91. [sm addTransitionFrom:@"response" to:@"error"];
  92. [sm addTransitionFrom:@"error" to:@"error"];
  93. [sm addTransitionFrom:@"error" to:@"logoutNode"];
  94. [sm addTransitionFrom:@"logoutNode" to:@"response"];
  95. [sm addTransitionFrom:@"error" to:@"response"];
  96. [sm addTransitionFrom:@"response" to:@"tagCloud"];
  97. [sm addTransitionFrom:@"tagCloud" to:@"tagCount"];
  98. [sm addTransitionFrom:@"tagCount" to:@"tagHref"];
  99. [sm addTransitionFrom:@"tagCount" to:@"tagCount"];
  100. [sm addTransitionFrom:@"tagHref" to:@"tagLabel"];
  101. [sm addTransitionFrom:@"tagLabel" to:@"tagLabel"];
  102. [sm addTransitionFrom:@"tagLabel" to:@"tagCloud"];
  103. [sm addTransitionFrom:@"tagCloud" to:@"response"];
  104. NSError *err = nil;
  105. [sm buildMachineWithStartState:@"response" error:&err];
  106. NSParameterAssert(nil == err);
  107. // MRLogD(@"%@", [sm descriptionDot], nil);
  108. d[M_STATE_MACHINE] = sm;
  109. htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(&FormField_Handler, (__bridge void *)d, (const char *)[data bytes], (int)data.length, "", XML_CHAR_ENCODING_NONE);
  110. htmlParseChunk(ctxt, "", 0, YES);
  111. htmlFreeParserCtxt(ctxt);
  112. // MRLogD(@"%@", d, nil);
  113. return d;
  114. }
  115. -(void)transitionError:(MROTransition *)t
  116. {
  117. ;
  118. }
  119. -(void)transitionField:(MROTransition *)t
  120. {
  121. NSAssert(NO, @"Not implemented yet.", nil);
  122. }
  123. -(void)transitionForm:(MROTransition *)t
  124. {
  125. NSAssert(NO, @"Not implemented yet.", nil);
  126. }
  127. -(void)transitionLogoutNode:(MROTransition *)t
  128. {
  129. NSAssert(NO, @"Not implemented yet.", nil);
  130. }
  131. -(void)transitionResponse:(MROTransition *)t
  132. {
  133. NSAssert(NO, @"Not implemented yet.", nil);
  134. }
  135. -(void)transitionTagCloud:(MROTransition *)t
  136. {
  137. NSAssert(NO, @"Not implemented yet.", nil);
  138. }
  139. -(void)transitionTagCount:(MROTransition *)t
  140. {
  141. NSAssert(NO, @"Not implemented yet.", nil);
  142. }
  143. -(void)transitionTagHref:(MROTransition *)t
  144. {
  145. NSAssert(NO, @"Not implemented yet.", nil);
  146. }
  147. -(void)transitionTagLabel:(MROTransition *)t
  148. {
  149. NSAssert(NO, @"Not implemented yet.", nil);
  150. }
  151. -(void)transitionTitle:(MROTransition *)t
  152. {
  153. NSAssert(NO, @"Not implemented yet.", nil);
  154. }
  155. #pragma mark foo
  156. -(void)_testBanned
  157. {
  158. NSDictionary *ret = [self parseShaarliHtml:[self dataWithContentsOfFixture:@"banned" withExtension:@"html"] error:nil]; // parseShaarliHtml([self dataWithContentsOfFixture:@"banned" withExtension:@"html"], nil);
  159. // MRLogD(@"%@", ret, nil);
  160. XCTAssertEqual(2, ret.count, @"entries' count");
  161. XCTAssertEqualObjects(@"links.mro", ret[M_TITLE], @"");
  162. XCTAssertEqualObjects(@"You have been banned from login after too many failed attempts. Try later.", ret[@"headerform"], @"");
  163. XCTAssertEqual(0, [ret[M_FORM] count], @"entries' count");
  164. }
  165. -(void)_testParseShaarliHtml
  166. {
  167. {
  168. NSDictionary *ret = [self parseShaarliHtml:[self dataWithContentsOfFixture:@"testLogin.ok" withExtension:@"html"] error:nil]; // parseShaarliHtml([self dataWithContentsOfFixture:@"testLogin.ok" withExtension:@"html"], nil);
  169. // MRLogD(@"%@", ret, nil);
  170. XCTAssertEqual(4, ret.count, @"entries' count");
  171. XCTAssertEqualObjects(@ (1), ret[M_HAS_LOGOUT], @"");
  172. XCTAssertEqualObjects(@"links.mro", ret[M_TITLE], @"");
  173. XCTAssertNotNil(ret[@"headerform"], @"");
  174. XCTAssertEqual(5, [ret[M_FORM] count], @"entries' count");
  175. XCTAssertEqualObjects(@"20150715_200440", ret[M_FORM][@"edit_link"], @"");
  176. XCTAssertEqualObjects(@"20150715_200440", ret[M_FORM][@"lf_linkdate"], @"");
  177. XCTAssertEqualObjects(@"", ret[M_FORM][@"searchtags"], @"");
  178. XCTAssertEqualObjects(@"", ret[M_FORM][@"searchterm"], @"");
  179. XCTAssertEqualObjects(@"6ff77552e09da9ef31e0e9d0b717da8933f68975", ret[M_FORM][F_K_TOKEN], @"");
  180. }
  181. {
  182. NSDictionary *ret = [self parseShaarliHtml:[self dataWithContentsOfFixture:@"testLogin.0" withExtension:@"html"] error:nil]; // parseShaarliHtml([self dataWithContentsOfFixture:@"testLogin.0" withExtension:@"html"], nil);
  183. // MRLogD(@"%@", ret, nil);
  184. XCTAssertEqual(3, ret.count, @"entries' count");
  185. XCTAssertEqualObjects(@"links.mro", ret[M_TITLE], @"");
  186. XCTAssertNotNil(ret[@"headerform"], @"");
  187. XCTAssertEqual(2, [ret[M_FORM] count], @"entries' count");
  188. XCTAssertEqualObjects(@"http://links.mro.name/", ret[M_FORM][F_K_RETURNURL], @"");
  189. XCTAssertEqualObjects(@"20119241badf78a3dcfa55ae58eab429a5d24bad", ret[M_FORM][F_K_TOKEN], @"");
  190. }
  191. {
  192. NSDictionary *ret = [self parseShaarliHtml:[self dataWithContentsOfFixture:@"05.addlink-1" withExtension:@"html"] error:nil]; // parseShaarliHtml([self dataWithContentsOfFixture:@"05.addlink-1" withExtension:@"html"], nil);
  193. // MRLogD(@"%@", ret, nil);
  194. XCTAssertEqual(4, ret.count, @"entries' count");
  195. XCTAssertEqualObjects(@"links.mro", ret[M_TITLE], @"");
  196. XCTAssertEqualObjects(@ (YES), ret[M_HAS_LOGOUT], @"");
  197. XCTAssertNotNil(ret[@"headerform"], @"");
  198. XCTAssertEqual(0, [ret[M_FORM] count], @"entries' count");
  199. }
  200. {
  201. NSDictionary *ret = [self parseShaarliHtml:[self dataWithContentsOfFixture:@"05.addlink-2" withExtension:@"html"] error:nil]; // parseShaarliHtml([self dataWithContentsOfFixture:@"05.addlink-2" withExtension:@"html"], nil);
  202. // MRLogD(@"%@", ret, nil);
  203. XCTAssertEqual(3, ret.count, @"entries' count");
  204. XCTAssertEqualObjects(@"links.mro", ret[M_TITLE], @"");
  205. XCTAssertEqualObjects(@ (YES), ret[M_HAS_LOGOUT], @"");
  206. XCTAssertEqual(8, [ret[M_FORM] count], @"entries' count");
  207. XCTAssertEqualObjects(@"Cancel", ret[M_FORM][@"cancel_edit"], @"");
  208. XCTAssertEqualObjects(@"20150719_173950", ret[M_FORM][@"lf_linkdate"], @"");
  209. XCTAssertEqualObjects(@"", ret[M_FORM][K_F_LF_TAGS], @"");
  210. XCTAssertEqualObjects(@"Note: ", ret[M_FORM][K_F_LF_TITLE], @"");
  211. XCTAssertEqualObjects(@"?tgI8rw", ret[M_FORM][K_F_LF_URL], @"");
  212. XCTAssertEqualObjects(@"http://links.mro.name/?do=login&post=", ret[M_FORM][F_K_RETURNURL], @"");
  213. XCTAssertEqualObjects(@"Save", ret[M_FORM][@"save_edit"], @"");
  214. XCTAssertEqualObjects(@"e90b4ab4846c221880872003ba47859183da4e6e", ret[M_FORM][F_K_TOKEN], @"");
  215. }
  216. }
  217. -(void)_testParseHtmlTags
  218. {
  219. NSDictionary *ret = [self parseShaarliHtml:[self dataWithContentsOfFixture:@"03.tagcloud" withExtension:@"html"] error:nil]; // parseShaarliHtml([self dataWithContentsOfFixture:@"03.tagcloud" withExtension:@"html"], nil);
  220. // MRLogD(@"%@", ret, nil);
  221. XCTAssertEqual(2, ret.count, @"entries' count");
  222. XCTAssertEqualObjects(@"links.mro", ret[M_TITLE], @"");
  223. NSArray *tags = ret[@"tags"];
  224. MRLogD(@"%@", tags, nil);
  225. XCTAssertEqual(1794, tags.count, @"");
  226. {
  227. NSArray *sorted = [tags sortedArrayUsingComparator:^NSComparisonResult (NSDictionary * t1, NSDictionary * t2) {
  228. const NSComparisonResult r0 = [t2[@"count"] compare:t1[@"count"]];
  229. if( r0 != NSOrderedSame )
  230. return r0;
  231. return [t1[@"label"] compare:t2[@"label"] options:0];
  232. }
  233. ];
  234. XCTAssertEqualObjects (@"Software", [sorted firstObject][@"label"], @"");
  235. XCTAssertEqualObjects (@ (170), [sorted firstObject][@"count"], @"");
  236. XCTAssertEqualObjects (@"§99StGB", [sorted lastObject][@"label"], @"");
  237. XCTAssertEqualObjects (@ (1), [sorted lastObject][@"count"], @"");
  238. }
  239. }
  240. @end