FIRAggregateTests.mm 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <FirebaseFirestore/FIRAggregateField.h>
  17. #import <FirebaseFirestore/FIRFieldPath.h>
  18. #import <FirebaseFirestore/FirebaseFirestore.h>
  19. #import <XCTest/XCTest.h>
  20. #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
  21. #import "Firestore/core/src/util/exception.h"
  22. @interface FIRAggregateTests : FSTIntegrationTestCase
  23. @end
  24. @implementation FIRAggregateTests
  25. - (void)testCountAggregateFieldQueryEquals {
  26. FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
  27. FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
  28. FIRAggregateQuery* query1 = [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  29. FIRAggregateQuery* query1Same =
  30. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  31. FIRAggregateQuery* query1DiffAgg =
  32. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  33. FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
  34. FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  35. aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  36. FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a"
  37. isEqualTo:@1] queryLimitedTo:100] count];
  38. FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
  39. aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  40. FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
  41. isEqualTo:@1] queryOrderedByField:@"c"] count];
  42. XCTAssertEqualObjects(query1, query1Same);
  43. XCTAssertEqualObjects(query2, query2Same);
  44. XCTAssertEqualObjects(query3, query3Same);
  45. XCTAssertEqual([query1 hash], [query1Same hash]);
  46. XCTAssertEqual([query2 hash], [query2Same hash]);
  47. XCTAssertEqual([query3 hash], [query3Same hash]);
  48. XCTAssertFalse([query1 isEqual:nil]);
  49. XCTAssertFalse([query1 isEqual:@"string"]);
  50. XCTAssertFalse([query1 isEqual:query2]);
  51. XCTAssertFalse([query2 isEqual:query3]);
  52. XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
  53. XCTAssertNotEqual([query1 hash], [query2 hash]);
  54. XCTAssertNotEqual([query2 hash], [query3 hash]);
  55. }
  56. - (void)testSumAggregateFieldQueryEquals {
  57. FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
  58. FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
  59. FIRAggregateQuery* query1 =
  60. [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  61. FIRAggregateQuery* query1Same =
  62. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  63. FIRAggregateQuery* query1WithFieldPath =
  64. [coll1Same aggregate:@[ [FIRAggregateField
  65. aggregateFieldForSumOfFieldPath:[[FIRFieldPath alloc]
  66. initWithFields:@[ @"baz" ]]] ]];
  67. FIRAggregateQuery* query1DiffAgg =
  68. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  69. FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
  70. FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  71. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  72. FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  73. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  74. FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
  75. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  76. FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
  77. isEqualTo:@1] queryOrderedByField:@"c"]
  78. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  79. XCTAssertEqualObjects(query1, query1Same);
  80. XCTAssertEqualObjects(query1, query1WithFieldPath);
  81. XCTAssertEqualObjects(query2, query2Same);
  82. XCTAssertEqualObjects(query3, query3Same);
  83. XCTAssertEqual([query1 hash], [query1Same hash]);
  84. XCTAssertEqual([query1 hash], [query1WithFieldPath hash]);
  85. XCTAssertEqual([query2 hash], [query2Same hash]);
  86. XCTAssertEqual([query3 hash], [query3Same hash]);
  87. XCTAssertFalse([query1 isEqual:nil]);
  88. XCTAssertFalse([query1 isEqual:@"string"]);
  89. XCTAssertFalse([query1 isEqual:query2]);
  90. XCTAssertFalse([query2 isEqual:query3]);
  91. XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
  92. XCTAssertNotEqual([query1 hash], [query2 hash]);
  93. XCTAssertNotEqual([query2 hash], [query3 hash]);
  94. }
  95. - (void)testAverageAggregateFieldQueryEquals {
  96. FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
  97. FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
  98. FIRAggregateQuery* query1 =
  99. [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  100. FIRAggregateQuery* query1Same =
  101. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  102. FIRAggregateQuery* query1WithFieldPath = [coll1Same aggregate:@[
  103. [FIRAggregateField
  104. aggregateFieldForAverageOfFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"baz" ]]]
  105. ]];
  106. FIRAggregateQuery* query1DiffAgg =
  107. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  108. FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
  109. FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  110. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  111. FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  112. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  113. FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
  114. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  115. FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
  116. isEqualTo:@1] queryOrderedByField:@"c"]
  117. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  118. XCTAssertEqualObjects(query1, query1Same);
  119. XCTAssertEqualObjects(query1, query1WithFieldPath);
  120. XCTAssertEqualObjects(query2, query2Same);
  121. XCTAssertEqualObjects(query3, query3Same);
  122. XCTAssertEqual([query1 hash], [query1Same hash]);
  123. XCTAssertEqual([query1 hash], [query1WithFieldPath hash]);
  124. XCTAssertEqual([query2 hash], [query2Same hash]);
  125. XCTAssertEqual([query3 hash], [query3Same hash]);
  126. XCTAssertFalse([query1 isEqual:nil]);
  127. XCTAssertFalse([query1 isEqual:@"string"]);
  128. XCTAssertFalse([query1 isEqual:query2]);
  129. XCTAssertFalse([query2 isEqual:query3]);
  130. XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
  131. XCTAssertNotEqual([query1 hash], [query2 hash]);
  132. XCTAssertNotEqual([query2 hash], [query3 hash]);
  133. }
  134. - (void)testAggregateFieldQueryNotEquals {
  135. FIRCollectionReference* coll = [self collectionRefWithDocuments:@{}];
  136. FIRAggregateQuery* query1 = [coll aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  137. FIRAggregateQuery* query2 =
  138. [coll aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  139. FIRAggregateQuery* query3 =
  140. [coll aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  141. XCTAssertNotEqualObjects(query1, query2);
  142. XCTAssertNotEqualObjects(query2, query3);
  143. XCTAssertNotEqualObjects(query3, query1);
  144. XCTAssertNotEqual([query1 hash], [query2 hash]);
  145. XCTAssertNotEqual([query2 hash], [query3 hash]);
  146. XCTAssertNotEqual([query3 hash], [query1 hash]);
  147. FIRQuery* baseQuery = [[[[coll documentWithPath:@"bar"] collectionWithPath:@"baz"]
  148. queryWhereField:@"a"
  149. isEqualTo:@1] queryLimitedTo:100];
  150. FIRAggregateQuery* query4 = [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  151. FIRAggregateQuery* query5 =
  152. [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  153. FIRAggregateQuery* query6 =
  154. [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  155. XCTAssertNotEqualObjects(query4, query5);
  156. XCTAssertNotEqualObjects(query5, query6);
  157. XCTAssertNotEqualObjects(query6, query4);
  158. XCTAssertNotEqual([query4 hash], [query5 hash]);
  159. XCTAssertNotEqual([query5 hash], [query6 hash]);
  160. XCTAssertNotEqual([query6 hash], [query4 hash]);
  161. }
  162. - (void)testCanRunAggregateQuery {
  163. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  164. @"a" : @{
  165. @"author" : @"authorA",
  166. @"title" : @"titleA",
  167. @"pages" : @100,
  168. @"height" : @24.5,
  169. @"weight" : @24.1,
  170. @"foo" : @1,
  171. @"bar" : @2,
  172. @"baz" : @3
  173. },
  174. @"b" : @{
  175. @"author" : @"authorB",
  176. @"title" : @"titleB",
  177. @"pages" : @50,
  178. @"height" : @25.5,
  179. @"weight" : @75.5,
  180. @"foo" : @1,
  181. @"bar" : @2,
  182. @"baz" : @3
  183. }
  184. }];
  185. FIRAggregateQuerySnapshot* snapshot =
  186. [self readSnapshotForAggregate:[testCollection aggregate:@[
  187. [FIRAggregateField aggregateFieldForCount],
  188. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  189. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  190. ]]];
  191. // Count
  192. XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
  193. [NSNumber numberWithLong:2L]);
  194. XCTAssertEqual([snapshot count], [NSNumber numberWithLong:2L]);
  195. // Sum
  196. XCTAssertEqual(
  197. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  198. [NSNumber numberWithLong:150L], );
  199. // Average
  200. XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
  201. aggregateFieldForAverageOfField:@"pages"]],
  202. [NSNumber numberWithDouble:75.0]);
  203. }
  204. - (void)testCanRunEmptyAggregateQuery {
  205. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  206. @"a" : @{
  207. @"author" : @"authorA",
  208. @"title" : @"titleA",
  209. @"pages" : @100,
  210. @"height" : @24.5,
  211. @"weight" : @24.1,
  212. @"foo" : @1,
  213. @"bar" : @2,
  214. @"baz" : @3
  215. },
  216. @"b" : @{
  217. @"author" : @"authorB",
  218. @"title" : @"titleB",
  219. @"pages" : @50,
  220. @"height" : @25.5,
  221. @"weight" : @75.5,
  222. @"foo" : @1,
  223. @"bar" : @2,
  224. @"baz" : @3
  225. }
  226. }];
  227. FIRAggregateQuery* emptyQuery = [testCollection aggregate:@[]];
  228. __block NSError* result;
  229. XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
  230. [emptyQuery aggregationWithSource:FIRAggregateSourceServer
  231. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  232. XCTAssertNil(snapshot);
  233. result = error;
  234. [expectation fulfill];
  235. }];
  236. [self awaitExpectation:expectation];
  237. XCTAssertNotNil(result);
  238. XCTAssertTrue([[result localizedDescription] containsString:@"Aggregations can not be empty"]);
  239. }
  240. // (TODO:b/283101111): Try thread sanitizer to see if timeout on Github Actions is gone.
  241. #if !defined(THREAD_SANITIZER)
  242. - (void)testAggregateFieldQuerySnapshotEquality {
  243. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  244. @"a" : @{
  245. @"author" : @"authorA",
  246. @"title" : @"titleA",
  247. @"pages" : @100,
  248. @"height" : @24.5,
  249. @"weight" : @24.1,
  250. @"foo" : @1,
  251. @"bar" : @2,
  252. @"baz" : @3
  253. },
  254. @"b" : @{
  255. @"author" : @"authorB",
  256. @"title" : @"titleB",
  257. @"pages" : @50,
  258. @"height" : @25.5,
  259. @"weight" : @75.5,
  260. @"foo" : @1,
  261. @"bar" : @2,
  262. @"baz" : @3
  263. }
  264. }];
  265. FIRAggregateQuerySnapshot* snapshot1 =
  266. [self readSnapshotForAggregate:[testCollection aggregate:@[
  267. [FIRAggregateField aggregateFieldForCount],
  268. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  269. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  270. ]]];
  271. FIRAggregateQuerySnapshot* snapshot2 =
  272. [self readSnapshotForAggregate:[testCollection aggregate:@[
  273. [FIRAggregateField aggregateFieldForCount],
  274. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  275. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  276. ]]];
  277. // different aggregates
  278. FIRAggregateQuerySnapshot* snapshot3 =
  279. [self readSnapshotForAggregate:[testCollection aggregate:@[
  280. [FIRAggregateField aggregateFieldForCount],
  281. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  282. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
  283. ]]];
  284. // different data set
  285. FIRAggregateQuerySnapshot* snapshot4 =
  286. [self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@50]
  287. aggregate:@[
  288. [FIRAggregateField aggregateFieldForCount],
  289. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  290. [FIRAggregateField
  291. aggregateFieldForAverageOfField:@"pages"]
  292. ]]];
  293. XCTAssertEqualObjects(snapshot1, snapshot2);
  294. XCTAssertNotEqualObjects(snapshot1, snapshot3);
  295. XCTAssertNotEqualObjects(snapshot1, snapshot4);
  296. XCTAssertNotEqualObjects(snapshot3, snapshot4);
  297. XCTAssertEqual([snapshot1 hash], [snapshot2 hash]);
  298. XCTAssertNotEqual([snapshot1 hash], [snapshot3 hash]);
  299. XCTAssertNotEqual([snapshot1 hash], [snapshot4 hash]);
  300. XCTAssertNotEqual([snapshot3 hash], [snapshot4 hash]);
  301. }
  302. #endif // #if !defined(THREAD_SANITIZER)
  303. - (void)testAggregateOnFieldNameWithMaxLength {
  304. // The longest field name and alias allowed is 1500 bytes or 1499 characters.
  305. NSString* longField = [@"" stringByPaddingToLength:1499
  306. withString:@"0123456789"
  307. startingAtIndex:0];
  308. FIRCollectionReference* testCollection =
  309. [self collectionRefWithDocuments:@{@"a" : @{longField : @1}, @"b" : @{longField : @2}}];
  310. FIRAggregateQuerySnapshot* snapshot =
  311. [self readSnapshotForAggregate:[testCollection
  312. aggregate:@[ [FIRAggregateField
  313. aggregateFieldForSumOfField:longField] ]]];
  314. // Sum
  315. XCTAssertEqual(
  316. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:longField]],
  317. [NSNumber numberWithLong:3], );
  318. }
  319. - (void)testCanGetDuplicateAggregations {
  320. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  321. @"a" : @{
  322. @"author" : @"authorA",
  323. @"title" : @"titleA",
  324. @"pages" : @100,
  325. @"height" : @24.5,
  326. @"weight" : @24.1,
  327. @"foo" : @1,
  328. @"bar" : @2,
  329. @"baz" : @3
  330. },
  331. @"b" : @{
  332. @"author" : @"authorB",
  333. @"title" : @"titleB",
  334. @"pages" : @50,
  335. @"height" : @25.5,
  336. @"weight" : @75.5,
  337. @"foo" : @1,
  338. @"bar" : @2,
  339. @"baz" : @3
  340. }
  341. }];
  342. FIRAggregateQuerySnapshot* snapshot =
  343. [self readSnapshotForAggregate:[testCollection aggregate:@[
  344. [FIRAggregateField aggregateFieldForCount],
  345. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  346. [FIRAggregateField aggregateFieldForSumOfField:@"pages"]
  347. ]]];
  348. // Count
  349. XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
  350. [NSNumber numberWithLong:2L]);
  351. // Sum
  352. XCTAssertEqual(
  353. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  354. [NSNumber numberWithLong:150L], );
  355. }
  356. - (void)testTerminateDoesNotCrashWithFlyingAggregateQuery {
  357. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  358. @"a" : @{
  359. @"author" : @"authorA",
  360. @"title" : @"titleA",
  361. @"pages" : @100,
  362. @"height" : @24.5,
  363. @"weight" : @24.1,
  364. @"foo" : @1,
  365. @"bar" : @2,
  366. @"baz" : @3
  367. },
  368. @"b" : @{
  369. @"author" : @"authorB",
  370. @"title" : @"titleB",
  371. @"pages" : @50,
  372. @"height" : @25.5,
  373. @"weight" : @75.5,
  374. @"foo" : @1,
  375. @"bar" : @2,
  376. @"baz" : @3
  377. }
  378. }];
  379. FIRAggregateQuery* aggQuery = [testCollection aggregate:@[
  380. [FIRAggregateField aggregateFieldForCount],
  381. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  382. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  383. ]];
  384. __block FIRAggregateQuerySnapshot* result;
  385. XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
  386. [aggQuery aggregationWithSource:FIRAggregateSourceServer
  387. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  388. XCTAssertNil(error);
  389. result = snapshot;
  390. [expectation fulfill];
  391. }];
  392. [self awaitExpectation:expectation];
  393. // Count
  394. XCTAssertEqual([result valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
  395. [NSNumber numberWithLong:2L]);
  396. // Sum
  397. XCTAssertEqual(
  398. [result valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  399. [NSNumber numberWithLong:150L], );
  400. }
  401. - (void)testCannotPerformMoreThanMaxAggregations {
  402. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  403. @"a" : @{
  404. @"author" : @"authorA",
  405. @"title" : @"titleA",
  406. @"pages" : @100,
  407. @"height" : @24.5,
  408. @"weight" : @24.1,
  409. @"foo" : @1,
  410. @"bar" : @2,
  411. @"baz" : @3
  412. },
  413. @"b" : @{
  414. @"author" : @"authorB",
  415. @"title" : @"titleB",
  416. @"pages" : @50,
  417. @"height" : @25.5,
  418. @"weight" : @75.5,
  419. @"foo" : @1,
  420. @"bar" : @2,
  421. @"baz" : @3
  422. }
  423. }];
  424. // Max is 5, we're attempting 6. I also like to live dangerously.
  425. FIRAggregateQuery* query = [testCollection aggregate:@[
  426. [FIRAggregateField aggregateFieldForCount],
  427. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  428. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  429. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  430. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"],
  431. [FIRAggregateField aggregateFieldForAverageOfField:@"foo"]
  432. ]];
  433. __block NSError* result;
  434. XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
  435. [query aggregationWithSource:FIRAggregateSourceServer
  436. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  437. XCTAssertNil(snapshot);
  438. result = error;
  439. [expectation fulfill];
  440. }];
  441. [self awaitExpectation:expectation];
  442. XCTAssertNotNil(result);
  443. if (!FSTIntegrationTestCase.isRunningAgainstEmulator) {
  444. XCTAssertTrue([[result localizedDescription] containsString:@"maximum number of aggregations"]);
  445. }
  446. }
  447. - (void)testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation {
  448. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  449. @"a" : @{
  450. @"author" : @"authorA",
  451. @"title" : @"titleA",
  452. @"pages" : @100,
  453. @"height" : @24.5,
  454. @"weight" : @24.1,
  455. @"foo" : @1,
  456. @"bar" : @2,
  457. @"baz" : @3
  458. },
  459. @"b" : @{
  460. @"author" : @"authorB",
  461. @"title" : @"titleB",
  462. @"pages" : @50,
  463. @"height" : @25.5,
  464. @"weight" : @75.5,
  465. @"foo" : @1,
  466. @"bar" : @2,
  467. @"baz" : @3
  468. }
  469. }];
  470. FIRAggregateQuerySnapshot* snapshot =
  471. [self readSnapshotForAggregate:[testCollection
  472. aggregate:@[ [FIRAggregateField
  473. aggregateFieldForSumOfField:@"pages"] ]]];
  474. @try {
  475. [snapshot count];
  476. XCTAssertTrue(false, "Exception expected");
  477. } @catch (NSException* exception) {
  478. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  479. XCTAssertEqualObjects(exception.reason,
  480. @"'count()' was not requested in the aggregation query.");
  481. }
  482. @
  483. try {
  484. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
  485. XCTAssertTrue(false, "Exception expected");
  486. } @catch (NSException* exception) {
  487. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  488. XCTAssertEqualObjects(exception.reason,
  489. @"'sum(foo)' was not requested in the aggregation query.");
  490. }
  491. @
  492. try {
  493. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
  494. XCTAssertTrue(false, "Exception expected");
  495. } @catch (NSException* exception) {
  496. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  497. XCTAssertEqualObjects(exception.reason,
  498. @"'avg(pages)' was not requested in the aggregation query.");
  499. }
  500. }
  501. - (void)testPerformsAggregationWhenUsingInOperator {
  502. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  503. @"a" : @{
  504. @"author" : @"authorA",
  505. @"title" : @"titleA",
  506. @"pages" : @100,
  507. @"year" : @1980,
  508. @"rating" : @5
  509. },
  510. @"b" : @{
  511. @"author" : @"authorB",
  512. @"title" : @"titleB",
  513. @"pages" : @50,
  514. @"year" : @2020,
  515. @"rating" : @4
  516. },
  517. @"c" : @{
  518. @"author" : @"authorC",
  519. @"title" : @"titleC",
  520. @"pages" : @100,
  521. @"year" : @1980,
  522. @"rating" : @3
  523. },
  524. @"d" : @{
  525. @"author" : @"authorD",
  526. @"title" : @"titleD",
  527. @"pages" : @50,
  528. @"year" : @2020,
  529. @"rating" : @0
  530. }
  531. }];
  532. FIRAggregateQuerySnapshot* snapshot = [self
  533. readSnapshotForAggregate:[[testCollection queryWhereField:@"rating" in:@[ @5, @3 ]]
  534. aggregate:@[
  535. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  536. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  537. [FIRAggregateField aggregateFieldForCount]
  538. ]]];
  539. // Count
  540. XCTAssertEqual(
  541. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  542. // Sum
  543. XCTAssertEqual(
  544. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  545. longValue],
  546. 8L);
  547. // Average
  548. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  549. aggregateFieldForAverageOfField:@"rating"]]
  550. doubleValue],
  551. 4.0);
  552. }
  553. - (void)testPerformsAggregationsOnNestedMapValues {
  554. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  555. @"a" : @{
  556. @"author" : @"authorA",
  557. @"title" : @"titleA",
  558. @"metadata" : @{@"pages" : @100, @"rating" : @{@"critic" : @2, @"user" : @5}}
  559. },
  560. @"b" : @{
  561. @"author" : @"authorB",
  562. @"title" : @"titleB",
  563. @"metadata" : @{@"pages" : @50, @"rating" : @{@"critic" : @4, @"user" : @4}}
  564. },
  565. }];
  566. FIRAggregateQuerySnapshot* snapshot =
  567. [self readSnapshotForAggregate:[testCollection aggregate:@[
  568. [FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"],
  569. [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"],
  570. [FIRAggregateField aggregateFieldForCount]
  571. ]]];
  572. // Count
  573. XCTAssertEqual(
  574. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  575. // Sum
  576. XCTAssertEqual(
  577. [[snapshot
  578. valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"]]
  579. longValue],
  580. 150L);
  581. // Average
  582. XCTAssertEqual(
  583. [[snapshot valueForAggregateField:[FIRAggregateField
  584. aggregateFieldForAverageOfField:@"metadata.pages"]]
  585. doubleValue],
  586. 75.0);
  587. }
  588. - (void)testPerformsSumThatOverflowsMaxLong {
  589. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  590. @"a" : @{
  591. @"author" : @"authorA",
  592. @"title" : @"titleA",
  593. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  594. },
  595. @"b" : @{
  596. @"author" : @"authorB",
  597. @"title" : @"titleB",
  598. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  599. },
  600. }];
  601. FIRAggregateQuerySnapshot* snapshot =
  602. [self readSnapshotForAggregate:[testCollection
  603. aggregate:@[ [FIRAggregateField
  604. aggregateFieldForSumOfField:@"rating"] ]]];
  605. // Sum
  606. XCTAssertEqual(
  607. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  608. doubleValue],
  609. [[NSNumber numberWithLong:LLONG_MAX] doubleValue] +
  610. [[NSNumber numberWithLong:LLONG_MAX] doubleValue]);
  611. }
  612. - (void)testPerformsSumThatCanOverflowLongValuesDuringAccumulation {
  613. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  614. @"a" : @{
  615. @"author" : @"authorA",
  616. @"title" : @"titleA",
  617. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  618. },
  619. @"b" : @{@"author" : @"authorB", @"title" : @"titleB", @"rating" : [NSNumber numberWithLong:1]},
  620. @"c" :
  621. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]}
  622. }];
  623. FIRAggregateQuerySnapshot* snapshot =
  624. [self readSnapshotForAggregate:[testCollection
  625. aggregate:@[ [FIRAggregateField
  626. aggregateFieldForSumOfField:@"rating"] ]]];
  627. // Sum
  628. XCTAssertEqual(
  629. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  630. longLongValue],
  631. [[NSNumber numberWithLong:LLONG_MAX - 100] longLongValue]);
  632. }
  633. - (void)testPerformsSumThatIsNegative {
  634. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  635. @"a" : @{
  636. @"author" : @"authorA",
  637. @"title" : @"titleA",
  638. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  639. },
  640. @"b" : @{
  641. @"author" : @"authorB",
  642. @"title" : @"titleB",
  643. @"rating" : [NSNumber numberWithLong:-LLONG_MAX]
  644. },
  645. @"c" :
  646. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]},
  647. @"d" : @{
  648. @"author" : @"authorD",
  649. @"title" : @"titleD",
  650. @"rating" : [NSNumber numberWithLong:-10000]
  651. }
  652. }];
  653. FIRAggregateQuerySnapshot* snapshot =
  654. [self readSnapshotForAggregate:[testCollection
  655. aggregate:@[ [FIRAggregateField
  656. aggregateFieldForSumOfField:@"rating"] ]]];
  657. // Sum
  658. XCTAssertEqual(
  659. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  660. longLongValue],
  661. [[NSNumber numberWithLong:-10101] longLongValue]);
  662. }
  663. - (void)testPerformsSumThatIsPositiveInfinity {
  664. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  665. @"a" : @{
  666. @"author" : @"authorA",
  667. @"title" : @"titleA",
  668. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  669. },
  670. @"b" : @{
  671. @"author" : @"authorB",
  672. @"title" : @"titleB",
  673. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  674. }
  675. }];
  676. FIRAggregateQuerySnapshot* snapshot =
  677. [self readSnapshotForAggregate:[testCollection
  678. aggregate:@[ [FIRAggregateField
  679. aggregateFieldForSumOfField:@"rating"] ]]];
  680. // Sum
  681. XCTAssertEqual(
  682. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  683. [NSNumber numberWithDouble:INFINITY]);
  684. }
  685. - (void)testPerformsSumThatIsNegativeInfinity {
  686. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  687. @"a" : @{
  688. @"author" : @"authorA",
  689. @"title" : @"titleA",
  690. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  691. },
  692. @"b" : @{
  693. @"author" : @"authorB",
  694. @"title" : @"titleB",
  695. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  696. }
  697. }];
  698. FIRAggregateQuerySnapshot* snapshot =
  699. [self readSnapshotForAggregate:[testCollection
  700. aggregate:@[ [FIRAggregateField
  701. aggregateFieldForSumOfField:@"rating"] ]]];
  702. // Sum
  703. XCTAssertEqual(
  704. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  705. [NSNumber numberWithDouble:-INFINITY]);
  706. }
  707. - (void)testPerformsSumThatIsValidButCouldOverflowDuringAggregation {
  708. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  709. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  710. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  711. @"c" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  712. @"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]}
  713. }];
  714. FIRAggregateQuerySnapshot* snapshot =
  715. [self readSnapshotForAggregate:[testCollection
  716. aggregate:@[ [FIRAggregateField
  717. aggregateFieldForSumOfField:@"rating"] ]]];
  718. // Sum
  719. long long ratingL =
  720. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  721. longLongValue];
  722. XCTAssertTrue(ratingL == [[NSNumber numberWithDouble:-INFINITY] longLongValue] || ratingL == 0 ||
  723. ratingL == [[NSNumber numberWithDouble:INFINITY] longLongValue]);
  724. double ratingD =
  725. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  726. doubleValue];
  727. XCTAssertTrue(ratingD == -INFINITY || ratingD == 0 || ratingD == INFINITY);
  728. }
  729. - (void)testPerformsSumOverResultSetOfZeroDocuments {
  730. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  731. @"a" : @{
  732. @"author" : @"authorA",
  733. @"title" : @"titleA",
  734. @"pages" : @100,
  735. @"height" : @24.5,
  736. @"weight" : @24.1,
  737. @"foo" : @1,
  738. @"bar" : @2,
  739. @"baz" : @3
  740. },
  741. @"b" : @{
  742. @"author" : @"authorB",
  743. @"title" : @"titleB",
  744. @"pages" : @50,
  745. @"height" : @25.5,
  746. @"weight" : @75.5,
  747. @"foo" : @1,
  748. @"bar" : @2,
  749. @"baz" : @3
  750. }
  751. }];
  752. FIRAggregateQuerySnapshot* snapshot =
  753. [self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  754. aggregate:@[ [FIRAggregateField
  755. aggregateFieldForSumOfField:@"pages"] ]]];
  756. // Sum
  757. XCTAssertEqual(
  758. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  759. [NSNumber numberWithLong:0L]);
  760. }
  761. - (void)testPerformsSumOnlyOnNumericFields {
  762. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  763. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  764. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  765. @"c" : @{@"rating" : @"3"},
  766. @"d" : @{@"rating" : [NSNumber numberWithLong:1]}
  767. }];
  768. FIRAggregateQuerySnapshot* snapshot =
  769. [self readSnapshotForAggregate:[testCollection aggregate:@[
  770. [FIRAggregateField aggregateFieldForCount],
  771. [FIRAggregateField aggregateFieldForSumOfField:@"rating"]
  772. ]]];
  773. // Count
  774. XCTAssertEqual(
  775. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  776. // Sum
  777. XCTAssertEqual(
  778. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  779. longLongValue],
  780. [[NSNumber numberWithLong:10] longLongValue]);
  781. }
  782. - (void)testPerformsSumOfMinIEEE754 {
  783. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  784. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  785. }];
  786. FIRAggregateQuerySnapshot* snapshot =
  787. [self readSnapshotForAggregate:[testCollection
  788. aggregate:@[ [FIRAggregateField
  789. aggregateFieldForSumOfField:@"rating"] ]]];
  790. // Sum
  791. XCTAssertEqual(
  792. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  793. doubleValue],
  794. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  795. }
  796. - (void)testPerformsAverageOfVariousNumericTypes {
  797. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  798. @"a" : @{
  799. @"x" : @1,
  800. @"intToInt" : [NSNumber numberWithLong:10],
  801. @"floatToInt" : [NSNumber numberWithDouble:10.5],
  802. @"mixedToInt" : [NSNumber numberWithLong:10],
  803. @"floatToFloat" : [NSNumber numberWithDouble:5.5],
  804. @"mixedToFloat" : [NSNumber numberWithDouble:8.6],
  805. @"intToFloat" : [NSNumber numberWithLong:10]
  806. },
  807. @"b" : @{
  808. @"intToInt" : [NSNumber numberWithLong:5],
  809. @"floatToInt" : [NSNumber numberWithDouble:9.5],
  810. @"mixedToInt" : [NSNumber numberWithDouble:9.5],
  811. @"floatToFloat" : [NSNumber numberWithDouble:4.5],
  812. @"mixedToFloat" : [NSNumber numberWithLong:9],
  813. @"intToFloat" : [NSNumber numberWithLong:9]
  814. },
  815. @"c" : @{
  816. @"intToInt" : [NSNumber numberWithLong:0],
  817. @"floatToInt" : @"ignore",
  818. @"mixedToInt" : [NSNumber numberWithDouble:10.5],
  819. @"floatToFloat" : [NSNumber numberWithDouble:3.5],
  820. @"mixedToFloat" : [NSNumber numberWithLong:10],
  821. @"intToFloat" : @"ignore"
  822. }
  823. }];
  824. NSArray* testCases = @[
  825. @{
  826. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToInt"],
  827. @"expected" : [NSNumber numberWithLong:5]
  828. },
  829. @{
  830. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToInt"],
  831. @"expected" : [NSNumber numberWithLong:10]
  832. },
  833. @{
  834. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToInt"],
  835. @"expected" : [NSNumber numberWithLong:10]
  836. },
  837. @{
  838. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToFloat"],
  839. @"expected" : [NSNumber numberWithDouble:4.5]
  840. },
  841. @{
  842. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToFloat"],
  843. @"expected" : [NSNumber numberWithDouble:9.2]
  844. },
  845. @{
  846. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToFloat"],
  847. @"expected" : [NSNumber numberWithDouble:9.5]
  848. }
  849. ];
  850. for (NSDictionary* testCase in testCases) {
  851. FIRAggregateQuerySnapshot* snapshot =
  852. [self readSnapshotForAggregate:[testCollection aggregate:@[ testCase[@"agg"] ]]];
  853. // Average
  854. XCTAssertEqual([[snapshot valueForAggregateField:testCase[@"agg"]] longValue],
  855. [testCase[@"expected"] longLongValue]);
  856. XCTAssertEqualWithAccuracy([[snapshot valueForAggregateField:testCase[@"agg"]] doubleValue],
  857. [testCase[@"expected"] doubleValue], 0.00000000000001);
  858. }
  859. }
  860. - (void)testPerformsAverageCausingUnderflow {
  861. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  862. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]},
  863. @"b" : @{@"rating" : [NSNumber numberWithDouble:0]}
  864. }];
  865. FIRAggregateQuerySnapshot* snapshot =
  866. [self readSnapshotForAggregate:[testCollection aggregate:@[
  867. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  868. ]]];
  869. // Average
  870. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  871. aggregateFieldForAverageOfField:@"rating"]]
  872. doubleValue],
  873. [[NSNumber numberWithDouble:0] doubleValue]);
  874. }
  875. - (void)testPerformsAverageOfMinIEEE754 {
  876. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  877. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  878. }];
  879. FIRAggregateQuerySnapshot* snapshot =
  880. [self readSnapshotForAggregate:[testCollection aggregate:@[
  881. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  882. ]]];
  883. // Average
  884. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  885. aggregateFieldForAverageOfField:@"rating"]]
  886. doubleValue],
  887. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  888. }
  889. - (void)testPerformsAverageOverflowIEEE754DuringAccumulation {
  890. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  891. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  892. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
  893. }];
  894. FIRAggregateQuerySnapshot* snapshot =
  895. [self readSnapshotForAggregate:[testCollection aggregate:@[
  896. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  897. ]]];
  898. // Average
  899. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  900. aggregateFieldForAverageOfField:@"rating"]]
  901. doubleValue],
  902. [[NSNumber numberWithDouble:INFINITY] doubleValue]);
  903. }
  904. - (void)testPerformsAverageOverResultSetOfZeroDocuments {
  905. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  906. @"a" : @{
  907. @"author" : @"authorA",
  908. @"title" : @"titleA",
  909. @"pages" : @100,
  910. @"height" : @24.5,
  911. @"weight" : @24.1,
  912. @"foo" : @1,
  913. @"bar" : @2,
  914. @"baz" : @3
  915. },
  916. @"b" : @{
  917. @"author" : @"authorB",
  918. @"title" : @"titleB",
  919. @"pages" : @50,
  920. @"height" : @25.5,
  921. @"weight" : @75.5,
  922. @"foo" : @1,
  923. @"bar" : @2,
  924. @"baz" : @3
  925. }
  926. }];
  927. FIRAggregateQuerySnapshot* snapshot = [self
  928. readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  929. aggregate:@[ [FIRAggregateField
  930. aggregateFieldForAverageOfField:@"pages"] ]]];
  931. // Average
  932. XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
  933. aggregateFieldForAverageOfField:@"pages"]],
  934. [NSNull null]);
  935. }
  936. - (void)testPerformsAverageOnlyOnNumericFields {
  937. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  938. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  939. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  940. @"c" : @{@"rating" : @"3"},
  941. @"d" : @{@"rating" : [NSNumber numberWithLong:6]}
  942. }];
  943. FIRAggregateQuerySnapshot* snapshot =
  944. [self readSnapshotForAggregate:[testCollection aggregate:@[
  945. [FIRAggregateField aggregateFieldForCount],
  946. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  947. ]]];
  948. // Count
  949. XCTAssertEqual(
  950. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  951. // Average
  952. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  953. aggregateFieldForAverageOfField:@"rating"]]
  954. doubleValue],
  955. [[NSNumber numberWithDouble:5] doubleValue]);
  956. }
  957. - (void)testFailWithMessageWithConsoleLinkIfMissingIndex {
  958. XCTSkipIf([FSTIntegrationTestCase isRunningAgainstEmulator],
  959. "Skip this test when running against the Firestore emulator because the Firestore "
  960. "emulator does not use indexes and never fails with a 'missing index' error.");
  961. FIRCollectionReference* testCollection = [self collectionRef];
  962. FIRQuery* compositeIndexQuery = [[testCollection queryWhereField:@"field1"
  963. isEqualTo:@42] queryWhereField:@"field2"
  964. isLessThan:@99];
  965. FIRAggregateQuery* compositeIndexAggregateQuery = [compositeIndexQuery aggregate:@[
  966. [FIRAggregateField aggregateFieldForCount],
  967. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  968. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  969. ]];
  970. XCTestExpectation* queryCompletion = [self expectationWithDescription:@"query"];
  971. [compositeIndexAggregateQuery
  972. aggregationWithSource:FIRAggregateSourceServer
  973. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  974. XCTAssertNotNil(error);
  975. if (error) {
  976. NSString* errorDescription = [error localizedDescription];
  977. XCTAssertTrue([errorDescription.lowercaseString containsString:@"index"],
  978. "The NSError should have contained the word 'index' "
  979. "(case-insensitive), but got: %@",
  980. errorDescription);
  981. // TODO(b/316359394) Remove this check for the default databases once
  982. // cl/582465034 is rolled out to production.
  983. if ([[FSTIntegrationTestCase databaseID] isEqualToString:@"(default)"]) {
  984. XCTAssertTrue(
  985. [errorDescription containsString:@"https://console.firebase.google.com"],
  986. "The NSError should have contained the string "
  987. "'https://console.firebase.google.com', but got: %@",
  988. errorDescription);
  989. }
  990. }
  991. XCTAssertNil(snapshot);
  992. [queryCompletion fulfill];
  993. }];
  994. [self awaitExpectations];
  995. }
  996. @end