FIRAggregateTests.mm 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  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. XCTAssertTrue([[result localizedDescription] containsString:@"maximum number of aggregations"]);
  444. }
  445. - (void)testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation {
  446. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  447. @"a" : @{
  448. @"author" : @"authorA",
  449. @"title" : @"titleA",
  450. @"pages" : @100,
  451. @"height" : @24.5,
  452. @"weight" : @24.1,
  453. @"foo" : @1,
  454. @"bar" : @2,
  455. @"baz" : @3
  456. },
  457. @"b" : @{
  458. @"author" : @"authorB",
  459. @"title" : @"titleB",
  460. @"pages" : @50,
  461. @"height" : @25.5,
  462. @"weight" : @75.5,
  463. @"foo" : @1,
  464. @"bar" : @2,
  465. @"baz" : @3
  466. }
  467. }];
  468. FIRAggregateQuerySnapshot* snapshot =
  469. [self readSnapshotForAggregate:[testCollection
  470. aggregate:@[ [FIRAggregateField
  471. aggregateFieldForSumOfField:@"pages"] ]]];
  472. @try {
  473. [snapshot count];
  474. XCTAssertTrue(false, "Exception expected");
  475. } @catch (NSException* exception) {
  476. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  477. XCTAssertEqualObjects(exception.reason,
  478. @"'count()' was not requested in the aggregation query.");
  479. }
  480. @
  481. try {
  482. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
  483. XCTAssertTrue(false, "Exception expected");
  484. } @catch (NSException* exception) {
  485. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  486. XCTAssertEqualObjects(exception.reason,
  487. @"'sum(foo)' was not requested in the aggregation query.");
  488. }
  489. @
  490. try {
  491. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
  492. XCTAssertTrue(false, "Exception expected");
  493. } @catch (NSException* exception) {
  494. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  495. XCTAssertEqualObjects(exception.reason,
  496. @"'avg(pages)' was not requested in the aggregation query.");
  497. }
  498. }
  499. - (void)testPerformsAggregationWhenUsingInOperator {
  500. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  501. @"a" : @{
  502. @"author" : @"authorA",
  503. @"title" : @"titleA",
  504. @"pages" : @100,
  505. @"year" : @1980,
  506. @"rating" : @5
  507. },
  508. @"b" : @{
  509. @"author" : @"authorB",
  510. @"title" : @"titleB",
  511. @"pages" : @50,
  512. @"year" : @2020,
  513. @"rating" : @4
  514. },
  515. @"c" : @{
  516. @"author" : @"authorC",
  517. @"title" : @"titleC",
  518. @"pages" : @100,
  519. @"year" : @1980,
  520. @"rating" : @3
  521. },
  522. @"d" : @{
  523. @"author" : @"authorD",
  524. @"title" : @"titleD",
  525. @"pages" : @50,
  526. @"year" : @2020,
  527. @"rating" : @0
  528. }
  529. }];
  530. FIRAggregateQuerySnapshot* snapshot = [self
  531. readSnapshotForAggregate:[[testCollection queryWhereField:@"rating" in:@[ @5, @3 ]]
  532. aggregate:@[
  533. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  534. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  535. [FIRAggregateField aggregateFieldForCount]
  536. ]]];
  537. // Count
  538. XCTAssertEqual(
  539. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  540. // Sum
  541. XCTAssertEqual(
  542. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  543. longValue],
  544. 8L);
  545. // Average
  546. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  547. aggregateFieldForAverageOfField:@"rating"]]
  548. doubleValue],
  549. 4.0);
  550. }
  551. - (void)testPerformsAggregationsOnNestedMapValues {
  552. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  553. @"a" : @{
  554. @"author" : @"authorA",
  555. @"title" : @"titleA",
  556. @"metadata" : @{@"pages" : @100, @"rating" : @{@"critic" : @2, @"user" : @5}}
  557. },
  558. @"b" : @{
  559. @"author" : @"authorB",
  560. @"title" : @"titleB",
  561. @"metadata" : @{@"pages" : @50, @"rating" : @{@"critic" : @4, @"user" : @4}}
  562. },
  563. }];
  564. FIRAggregateQuerySnapshot* snapshot =
  565. [self readSnapshotForAggregate:[testCollection aggregate:@[
  566. [FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"],
  567. [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"],
  568. [FIRAggregateField aggregateFieldForCount]
  569. ]]];
  570. // Count
  571. XCTAssertEqual(
  572. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  573. // Sum
  574. XCTAssertEqual(
  575. [[snapshot
  576. valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"]]
  577. longValue],
  578. 150L);
  579. // Average
  580. XCTAssertEqual(
  581. [[snapshot valueForAggregateField:[FIRAggregateField
  582. aggregateFieldForAverageOfField:@"metadata.pages"]]
  583. doubleValue],
  584. 75.0);
  585. }
  586. - (void)testPerformsSumThatOverflowsMaxLong {
  587. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  588. @"a" : @{
  589. @"author" : @"authorA",
  590. @"title" : @"titleA",
  591. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  592. },
  593. @"b" : @{
  594. @"author" : @"authorB",
  595. @"title" : @"titleB",
  596. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  597. },
  598. }];
  599. FIRAggregateQuerySnapshot* snapshot =
  600. [self readSnapshotForAggregate:[testCollection
  601. aggregate:@[ [FIRAggregateField
  602. aggregateFieldForSumOfField:@"rating"] ]]];
  603. // Sum
  604. XCTAssertEqual(
  605. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  606. doubleValue],
  607. [[NSNumber numberWithLong:LLONG_MAX] doubleValue] +
  608. [[NSNumber numberWithLong:LLONG_MAX] doubleValue]);
  609. }
  610. - (void)testPerformsSumThatCanOverflowLongValuesDuringAccumulation {
  611. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  612. @"a" : @{
  613. @"author" : @"authorA",
  614. @"title" : @"titleA",
  615. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  616. },
  617. @"b" : @{@"author" : @"authorB", @"title" : @"titleB", @"rating" : [NSNumber numberWithLong:1]},
  618. @"c" :
  619. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]}
  620. }];
  621. FIRAggregateQuerySnapshot* snapshot =
  622. [self readSnapshotForAggregate:[testCollection
  623. aggregate:@[ [FIRAggregateField
  624. aggregateFieldForSumOfField:@"rating"] ]]];
  625. // Sum
  626. XCTAssertEqual(
  627. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  628. longLongValue],
  629. [[NSNumber numberWithLong:LLONG_MAX - 100] longLongValue]);
  630. }
  631. - (void)testPerformsSumThatIsNegative {
  632. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  633. @"a" : @{
  634. @"author" : @"authorA",
  635. @"title" : @"titleA",
  636. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  637. },
  638. @"b" : @{
  639. @"author" : @"authorB",
  640. @"title" : @"titleB",
  641. @"rating" : [NSNumber numberWithLong:-LLONG_MAX]
  642. },
  643. @"c" :
  644. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]},
  645. @"d" : @{
  646. @"author" : @"authorD",
  647. @"title" : @"titleD",
  648. @"rating" : [NSNumber numberWithLong:-10000]
  649. }
  650. }];
  651. FIRAggregateQuerySnapshot* snapshot =
  652. [self readSnapshotForAggregate:[testCollection
  653. aggregate:@[ [FIRAggregateField
  654. aggregateFieldForSumOfField:@"rating"] ]]];
  655. // Sum
  656. XCTAssertEqual(
  657. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  658. longLongValue],
  659. [[NSNumber numberWithLong:-10101] longLongValue]);
  660. }
  661. - (void)testPerformsSumThatIsPositiveInfinity {
  662. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  663. @"a" : @{
  664. @"author" : @"authorA",
  665. @"title" : @"titleA",
  666. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  667. },
  668. @"b" : @{
  669. @"author" : @"authorB",
  670. @"title" : @"titleB",
  671. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  672. }
  673. }];
  674. FIRAggregateQuerySnapshot* snapshot =
  675. [self readSnapshotForAggregate:[testCollection
  676. aggregate:@[ [FIRAggregateField
  677. aggregateFieldForSumOfField:@"rating"] ]]];
  678. // Sum
  679. XCTAssertEqual(
  680. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  681. [NSNumber numberWithDouble:INFINITY]);
  682. }
  683. - (void)testPerformsSumThatIsNegativeInfinity {
  684. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  685. @"a" : @{
  686. @"author" : @"authorA",
  687. @"title" : @"titleA",
  688. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  689. },
  690. @"b" : @{
  691. @"author" : @"authorB",
  692. @"title" : @"titleB",
  693. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  694. }
  695. }];
  696. FIRAggregateQuerySnapshot* snapshot =
  697. [self readSnapshotForAggregate:[testCollection
  698. aggregate:@[ [FIRAggregateField
  699. aggregateFieldForSumOfField:@"rating"] ]]];
  700. // Sum
  701. XCTAssertEqual(
  702. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  703. [NSNumber numberWithDouble:-INFINITY]);
  704. }
  705. - (void)testPerformsSumThatIsValidButCouldOverflowDuringAggregation {
  706. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  707. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  708. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  709. @"c" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  710. @"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]}
  711. }];
  712. FIRAggregateQuerySnapshot* snapshot =
  713. [self readSnapshotForAggregate:[testCollection
  714. aggregate:@[ [FIRAggregateField
  715. aggregateFieldForSumOfField:@"rating"] ]]];
  716. // Sum
  717. long long ratingL =
  718. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  719. longLongValue];
  720. XCTAssertTrue(ratingL == [[NSNumber numberWithDouble:-INFINITY] longLongValue] || ratingL == 0 ||
  721. ratingL == [[NSNumber numberWithDouble:INFINITY] longLongValue]);
  722. double ratingD =
  723. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  724. doubleValue];
  725. XCTAssertTrue(ratingD == -INFINITY || ratingD == 0 || ratingD == INFINITY);
  726. }
  727. - (void)testPerformsSumOverResultSetOfZeroDocuments {
  728. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  729. @"a" : @{
  730. @"author" : @"authorA",
  731. @"title" : @"titleA",
  732. @"pages" : @100,
  733. @"height" : @24.5,
  734. @"weight" : @24.1,
  735. @"foo" : @1,
  736. @"bar" : @2,
  737. @"baz" : @3
  738. },
  739. @"b" : @{
  740. @"author" : @"authorB",
  741. @"title" : @"titleB",
  742. @"pages" : @50,
  743. @"height" : @25.5,
  744. @"weight" : @75.5,
  745. @"foo" : @1,
  746. @"bar" : @2,
  747. @"baz" : @3
  748. }
  749. }];
  750. FIRAggregateQuerySnapshot* snapshot =
  751. [self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  752. aggregate:@[ [FIRAggregateField
  753. aggregateFieldForSumOfField:@"pages"] ]]];
  754. // Sum
  755. XCTAssertEqual(
  756. [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  757. [NSNumber numberWithLong:0L]);
  758. }
  759. - (void)testPerformsSumOnlyOnNumericFields {
  760. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  761. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  762. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  763. @"c" : @{@"rating" : @"3"},
  764. @"d" : @{@"rating" : [NSNumber numberWithLong:1]}
  765. }];
  766. FIRAggregateQuerySnapshot* snapshot =
  767. [self readSnapshotForAggregate:[testCollection aggregate:@[
  768. [FIRAggregateField aggregateFieldForCount],
  769. [FIRAggregateField aggregateFieldForSumOfField:@"rating"]
  770. ]]];
  771. // Count
  772. XCTAssertEqual(
  773. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  774. // Sum
  775. XCTAssertEqual(
  776. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  777. longLongValue],
  778. [[NSNumber numberWithLong:10] longLongValue]);
  779. }
  780. - (void)testPerformsSumOfMinIEEE754 {
  781. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  782. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  783. }];
  784. FIRAggregateQuerySnapshot* snapshot =
  785. [self readSnapshotForAggregate:[testCollection
  786. aggregate:@[ [FIRAggregateField
  787. aggregateFieldForSumOfField:@"rating"] ]]];
  788. // Sum
  789. XCTAssertEqual(
  790. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  791. doubleValue],
  792. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  793. }
  794. - (void)testPerformsAverageOfVariousNumericTypes {
  795. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  796. @"a" : @{
  797. @"x" : @1,
  798. @"intToInt" : [NSNumber numberWithLong:10],
  799. @"floatToInt" : [NSNumber numberWithDouble:10.5],
  800. @"mixedToInt" : [NSNumber numberWithLong:10],
  801. @"floatToFloat" : [NSNumber numberWithDouble:5.5],
  802. @"mixedToFloat" : [NSNumber numberWithDouble:8.6],
  803. @"intToFloat" : [NSNumber numberWithLong:10]
  804. },
  805. @"b" : @{
  806. @"intToInt" : [NSNumber numberWithLong:5],
  807. @"floatToInt" : [NSNumber numberWithDouble:9.5],
  808. @"mixedToInt" : [NSNumber numberWithDouble:9.5],
  809. @"floatToFloat" : [NSNumber numberWithDouble:4.5],
  810. @"mixedToFloat" : [NSNumber numberWithLong:9],
  811. @"intToFloat" : [NSNumber numberWithLong:9]
  812. },
  813. @"c" : @{
  814. @"intToInt" : [NSNumber numberWithLong:0],
  815. @"floatToInt" : @"ignore",
  816. @"mixedToInt" : [NSNumber numberWithDouble:10.5],
  817. @"floatToFloat" : [NSNumber numberWithDouble:3.5],
  818. @"mixedToFloat" : [NSNumber numberWithLong:10],
  819. @"intToFloat" : @"ignore"
  820. }
  821. }];
  822. NSArray* testCases = @[
  823. @{
  824. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToInt"],
  825. @"expected" : [NSNumber numberWithLong:5]
  826. },
  827. @{
  828. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToInt"],
  829. @"expected" : [NSNumber numberWithLong:10]
  830. },
  831. @{
  832. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToInt"],
  833. @"expected" : [NSNumber numberWithLong:10]
  834. },
  835. @{
  836. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToFloat"],
  837. @"expected" : [NSNumber numberWithDouble:4.5]
  838. },
  839. @{
  840. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToFloat"],
  841. @"expected" : [NSNumber numberWithDouble:9.2]
  842. },
  843. @{
  844. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToFloat"],
  845. @"expected" : [NSNumber numberWithDouble:9.5]
  846. }
  847. ];
  848. for (NSDictionary* testCase in testCases) {
  849. FIRAggregateQuerySnapshot* snapshot =
  850. [self readSnapshotForAggregate:[testCollection aggregate:@[ testCase[@"agg"] ]]];
  851. // Average
  852. XCTAssertEqual([[snapshot valueForAggregateField:testCase[@"agg"]] longValue],
  853. [testCase[@"expected"] longLongValue]);
  854. XCTAssertEqualWithAccuracy([[snapshot valueForAggregateField:testCase[@"agg"]] doubleValue],
  855. [testCase[@"expected"] doubleValue], 0.00000000000001);
  856. }
  857. }
  858. - (void)testPerformsAverageCausingUnderflow {
  859. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  860. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]},
  861. @"b" : @{@"rating" : [NSNumber numberWithDouble:0]}
  862. }];
  863. FIRAggregateQuerySnapshot* snapshot =
  864. [self readSnapshotForAggregate:[testCollection aggregate:@[
  865. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  866. ]]];
  867. // Average
  868. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  869. aggregateFieldForAverageOfField:@"rating"]]
  870. doubleValue],
  871. [[NSNumber numberWithDouble:0] doubleValue]);
  872. }
  873. - (void)testPerformsAverageOfMinIEEE754 {
  874. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  875. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  876. }];
  877. FIRAggregateQuerySnapshot* snapshot =
  878. [self readSnapshotForAggregate:[testCollection aggregate:@[
  879. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  880. ]]];
  881. // Average
  882. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  883. aggregateFieldForAverageOfField:@"rating"]]
  884. doubleValue],
  885. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  886. }
  887. - (void)testPerformsAverageOverflowIEEE754DuringAccumulation {
  888. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  889. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  890. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
  891. }];
  892. FIRAggregateQuerySnapshot* snapshot =
  893. [self readSnapshotForAggregate:[testCollection aggregate:@[
  894. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  895. ]]];
  896. // Average
  897. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  898. aggregateFieldForAverageOfField:@"rating"]]
  899. doubleValue],
  900. [[NSNumber numberWithDouble:INFINITY] doubleValue]);
  901. }
  902. - (void)testPerformsAverageOverResultSetOfZeroDocuments {
  903. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  904. @"a" : @{
  905. @"author" : @"authorA",
  906. @"title" : @"titleA",
  907. @"pages" : @100,
  908. @"height" : @24.5,
  909. @"weight" : @24.1,
  910. @"foo" : @1,
  911. @"bar" : @2,
  912. @"baz" : @3
  913. },
  914. @"b" : @{
  915. @"author" : @"authorB",
  916. @"title" : @"titleB",
  917. @"pages" : @50,
  918. @"height" : @25.5,
  919. @"weight" : @75.5,
  920. @"foo" : @1,
  921. @"bar" : @2,
  922. @"baz" : @3
  923. }
  924. }];
  925. FIRAggregateQuerySnapshot* snapshot = [self
  926. readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  927. aggregate:@[ [FIRAggregateField
  928. aggregateFieldForAverageOfField:@"pages"] ]]];
  929. // Average
  930. XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
  931. aggregateFieldForAverageOfField:@"pages"]],
  932. [NSNull null]);
  933. }
  934. - (void)testPerformsAverageOnlyOnNumericFields {
  935. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  936. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  937. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  938. @"c" : @{@"rating" : @"3"},
  939. @"d" : @{@"rating" : [NSNumber numberWithLong:6]}
  940. }];
  941. FIRAggregateQuerySnapshot* snapshot =
  942. [self readSnapshotForAggregate:[testCollection aggregate:@[
  943. [FIRAggregateField aggregateFieldForCount],
  944. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  945. ]]];
  946. // Count
  947. XCTAssertEqual(
  948. [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  949. // Average
  950. XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
  951. aggregateFieldForAverageOfField:@"rating"]]
  952. doubleValue],
  953. [[NSNumber numberWithDouble:5] doubleValue]);
  954. }
  955. - (void)testFailWithMessageWithConsoleLinkIfMissingIndex {
  956. XCTSkipIf([FSTIntegrationTestCase isRunningAgainstEmulator],
  957. "Skip this test when running against the Firestore emulator because the Firestore "
  958. "emulator does not use indexes and never fails with a 'missing index' error.");
  959. FIRCollectionReference* testCollection = [self collectionRef];
  960. FIRQuery* compositeIndexQuery = [[testCollection queryWhereField:@"field1"
  961. isEqualTo:@42] queryWhereField:@"field2"
  962. isLessThan:@99];
  963. FIRAggregateQuery* compositeIndexAggregateQuery = [compositeIndexQuery aggregate:@[
  964. [FIRAggregateField aggregateFieldForCount],
  965. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  966. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  967. ]];
  968. XCTestExpectation* queryCompletion = [self expectationWithDescription:@"query"];
  969. [compositeIndexAggregateQuery
  970. aggregationWithSource:FIRAggregateSourceServer
  971. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  972. XCTAssertNotNil(error);
  973. if (error) {
  974. NSString* errorDescription = [error localizedDescription];
  975. XCTAssertTrue([errorDescription.lowercaseString containsString:@"index"],
  976. "The NSError should have contained the word 'index' "
  977. "(case-insensitive), but got: %@",
  978. errorDescription);
  979. // TODO(b/316359394) Remove this check for the default databases once
  980. // cl/582465034 is rolled out to production.
  981. if ([[FSTIntegrationTestCase databaseID] isEqualToString:@"(default)"]) {
  982. XCTAssertTrue(
  983. [errorDescription containsString:@"https://console.firebase.google.com"],
  984. "The NSError should have contained the string "
  985. "'https://console.firebase.google.com', but got: %@",
  986. errorDescription);
  987. }
  988. }
  989. XCTAssertNil(snapshot);
  990. [queryCompletion fulfill];
  991. }];
  992. [self awaitExpectations];
  993. }
  994. @end