FIRAggregateTests.mm 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444
  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/FIRFieldPath.h>
  17. #import <FirebaseFirestore/FirebaseFirestore.h>
  18. #import <XCTest/XCTest.h>
  19. // TODO(sum/avg) update these imports with public imports when sum/avg is public
  20. #import "Firestore/Source/API/FIRAggregateField.h"
  21. #import "Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h"
  22. #import "Firestore/Source/API/FIRQuery+Internal.h"
  23. #include "Firestore/core/src/util/sanitizers.h"
  24. #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
  25. @interface FIRAggregateTests : FSTIntegrationTestCase
  26. @end
  27. @implementation FIRAggregateTests
  28. - (void)testCountAggregateFieldQueryEquals {
  29. FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
  30. FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
  31. FIRAggregateQuery* query1 = [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  32. FIRAggregateQuery* query1Same =
  33. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  34. FIRAggregateQuery* query1DiffAgg =
  35. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  36. FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
  37. FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  38. aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  39. FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a"
  40. isEqualTo:@1] queryLimitedTo:100] count];
  41. FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
  42. aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  43. FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
  44. isEqualTo:@1] queryOrderedByField:@"c"] count];
  45. XCTAssertEqualObjects(query1, query1Same);
  46. XCTAssertEqualObjects(query2, query2Same);
  47. XCTAssertEqualObjects(query3, query3Same);
  48. XCTAssertEqual([query1 hash], [query1Same hash]);
  49. XCTAssertEqual([query2 hash], [query2Same hash]);
  50. XCTAssertEqual([query3 hash], [query3Same hash]);
  51. XCTAssertFalse([query1 isEqual:nil]);
  52. XCTAssertFalse([query1 isEqual:@"string"]);
  53. XCTAssertFalse([query1 isEqual:query2]);
  54. XCTAssertFalse([query2 isEqual:query3]);
  55. XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
  56. XCTAssertNotEqual([query1 hash], [query2 hash]);
  57. XCTAssertNotEqual([query2 hash], [query3 hash]);
  58. }
  59. - (void)testSumAggregateFieldQueryEquals {
  60. FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
  61. FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
  62. FIRAggregateQuery* query1 =
  63. [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  64. FIRAggregateQuery* query1Same =
  65. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  66. FIRAggregateQuery* query1WithFieldPath =
  67. [coll1Same aggregate:@[ [FIRAggregateField
  68. aggregateFieldForSumOfFieldPath:[[FIRFieldPath alloc]
  69. initWithFields:@[ @"baz" ]]] ]];
  70. FIRAggregateQuery* query1DiffAgg =
  71. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  72. FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
  73. FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  74. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  75. FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  76. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  77. FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
  78. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  79. FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
  80. isEqualTo:@1] queryOrderedByField:@"c"]
  81. aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  82. XCTAssertEqualObjects(query1, query1Same);
  83. XCTAssertEqualObjects(query1, query1WithFieldPath);
  84. XCTAssertEqualObjects(query2, query2Same);
  85. XCTAssertEqualObjects(query3, query3Same);
  86. XCTAssertEqual([query1 hash], [query1Same hash]);
  87. XCTAssertEqual([query1 hash], [query1WithFieldPath hash]);
  88. XCTAssertEqual([query2 hash], [query2Same hash]);
  89. XCTAssertEqual([query3 hash], [query3Same hash]);
  90. XCTAssertFalse([query1 isEqual:nil]);
  91. XCTAssertFalse([query1 isEqual:@"string"]);
  92. XCTAssertFalse([query1 isEqual:query2]);
  93. XCTAssertFalse([query2 isEqual:query3]);
  94. XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
  95. XCTAssertNotEqual([query1 hash], [query2 hash]);
  96. XCTAssertNotEqual([query2 hash], [query3 hash]);
  97. }
  98. - (void)testAverageAggregateFieldQueryEquals {
  99. FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
  100. FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
  101. FIRAggregateQuery* query1 =
  102. [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  103. FIRAggregateQuery* query1Same =
  104. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  105. FIRAggregateQuery* query1WithFieldPath = [coll1Same aggregate:@[
  106. [FIRAggregateField
  107. aggregateFieldForAverageOfFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"baz" ]]]
  108. ]];
  109. FIRAggregateQuery* query1DiffAgg =
  110. [coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  111. FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
  112. FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  113. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  114. FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
  115. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  116. FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
  117. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  118. FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
  119. isEqualTo:@1] queryOrderedByField:@"c"]
  120. aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  121. XCTAssertEqualObjects(query1, query1Same);
  122. XCTAssertEqualObjects(query1, query1WithFieldPath);
  123. XCTAssertEqualObjects(query2, query2Same);
  124. XCTAssertEqualObjects(query3, query3Same);
  125. XCTAssertEqual([query1 hash], [query1Same hash]);
  126. XCTAssertEqual([query1 hash], [query1WithFieldPath hash]);
  127. XCTAssertEqual([query2 hash], [query2Same hash]);
  128. XCTAssertEqual([query3 hash], [query3Same hash]);
  129. XCTAssertFalse([query1 isEqual:nil]);
  130. XCTAssertFalse([query1 isEqual:@"string"]);
  131. XCTAssertFalse([query1 isEqual:query2]);
  132. XCTAssertFalse([query2 isEqual:query3]);
  133. XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
  134. XCTAssertNotEqual([query1 hash], [query2 hash]);
  135. XCTAssertNotEqual([query2 hash], [query3 hash]);
  136. }
  137. - (void)testAggregateFieldQueryNotEquals {
  138. FIRCollectionReference* coll = [self collectionRefWithDocuments:@{}];
  139. FIRAggregateQuery* query1 = [coll aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  140. FIRAggregateQuery* query2 =
  141. [coll aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  142. FIRAggregateQuery* query3 =
  143. [coll aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  144. XCTAssertNotEqualObjects(query1, query2);
  145. XCTAssertNotEqualObjects(query2, query3);
  146. XCTAssertNotEqualObjects(query3, query1);
  147. XCTAssertNotEqual([query1 hash], [query2 hash]);
  148. XCTAssertNotEqual([query2 hash], [query3 hash]);
  149. XCTAssertNotEqual([query3 hash], [query1 hash]);
  150. FIRQuery* baseQuery = [[[[coll documentWithPath:@"bar"] collectionWithPath:@"baz"]
  151. queryWhereField:@"a"
  152. isEqualTo:@1] queryLimitedTo:100];
  153. FIRAggregateQuery* query4 = [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
  154. FIRAggregateQuery* query5 =
  155. [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
  156. FIRAggregateQuery* query6 =
  157. [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
  158. XCTAssertNotEqualObjects(query4, query5);
  159. XCTAssertNotEqualObjects(query5, query6);
  160. XCTAssertNotEqualObjects(query6, query4);
  161. XCTAssertNotEqual([query4 hash], [query5 hash]);
  162. XCTAssertNotEqual([query5 hash], [query6 hash]);
  163. XCTAssertNotEqual([query6 hash], [query4 hash]);
  164. }
  165. - (void)testCanRunAggregateQuery {
  166. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  167. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  168. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  169. @"a" : @{
  170. @"author" : @"authorA",
  171. @"title" : @"titleA",
  172. @"pages" : @100,
  173. @"height" : @24.5,
  174. @"weight" : @24.1,
  175. @"foo" : @1,
  176. @"bar" : @2,
  177. @"baz" : @3
  178. },
  179. @"b" : @{
  180. @"author" : @"authorB",
  181. @"title" : @"titleB",
  182. @"pages" : @50,
  183. @"height" : @25.5,
  184. @"weight" : @75.5,
  185. @"foo" : @1,
  186. @"bar" : @2,
  187. @"baz" : @3
  188. }
  189. }];
  190. FIRAggregateQuerySnapshot* snapshot =
  191. [self readSnapshotForAggregate:[testCollection aggregate:@[
  192. [FIRAggregateField aggregateFieldForCount],
  193. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  194. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  195. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  196. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
  197. ]]];
  198. // Count
  199. XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
  200. [NSNumber numberWithLong:2L]);
  201. XCTAssertEqual([snapshot count], [NSNumber numberWithLong:2L]);
  202. // Sum
  203. XCTAssertEqual(
  204. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  205. [NSNumber numberWithLong:150L], );
  206. XCTAssertEqual(
  207. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"weight"]]
  208. doubleValue],
  209. 99.6);
  210. // Average
  211. XCTAssertEqual(
  212. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
  213. [NSNumber numberWithDouble:75.0]);
  214. XCTAssertEqual(
  215. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"weight"]]
  216. doubleValue],
  217. 49.8);
  218. }
  219. - (void)testCanRunEmptyAggregateQuery {
  220. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  221. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  222. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  223. @"a" : @{
  224. @"author" : @"authorA",
  225. @"title" : @"titleA",
  226. @"pages" : @100,
  227. @"height" : @24.5,
  228. @"weight" : @24.1,
  229. @"foo" : @1,
  230. @"bar" : @2,
  231. @"baz" : @3
  232. },
  233. @"b" : @{
  234. @"author" : @"authorB",
  235. @"title" : @"titleB",
  236. @"pages" : @50,
  237. @"height" : @25.5,
  238. @"weight" : @75.5,
  239. @"foo" : @1,
  240. @"bar" : @2,
  241. @"baz" : @3
  242. }
  243. }];
  244. FIRAggregateQuery* emptyQuery = [testCollection aggregate:@[]];
  245. __block NSError* result;
  246. XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
  247. [emptyQuery aggregationWithSource:FIRAggregateSourceServer
  248. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  249. XCTAssertNil(snapshot);
  250. result = error;
  251. [expectation fulfill];
  252. }];
  253. [self awaitExpectation:expectation];
  254. XCTAssertNotNil(result);
  255. XCTAssertTrue([[result localizedDescription] containsString:@"Aggregations can not be empty"]);
  256. }
  257. // (TODO:b/283101111): Try thread sanitizer to see if timeout on Github Actions is gone.
  258. #if !defined(THREAD_SANITIZER)
  259. - (void)testAggregateFieldQuerySnapshotEquality {
  260. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  261. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  262. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  263. @"a" : @{
  264. @"author" : @"authorA",
  265. @"title" : @"titleA",
  266. @"pages" : @100,
  267. @"height" : @24.5,
  268. @"weight" : @24.1,
  269. @"foo" : @1,
  270. @"bar" : @2,
  271. @"baz" : @3
  272. },
  273. @"b" : @{
  274. @"author" : @"authorB",
  275. @"title" : @"titleB",
  276. @"pages" : @50,
  277. @"height" : @25.5,
  278. @"weight" : @75.5,
  279. @"foo" : @1,
  280. @"bar" : @2,
  281. @"baz" : @3
  282. }
  283. }];
  284. FIRAggregateQuerySnapshot* snapshot1 =
  285. [self readSnapshotForAggregate:[testCollection aggregate:@[
  286. [FIRAggregateField aggregateFieldForCount],
  287. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  288. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  289. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  290. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
  291. ]]];
  292. FIRAggregateQuerySnapshot* snapshot2 =
  293. [self readSnapshotForAggregate:[testCollection aggregate:@[
  294. [FIRAggregateField aggregateFieldForCount],
  295. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  296. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  297. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  298. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
  299. ]]];
  300. // different aggregates
  301. FIRAggregateQuerySnapshot* snapshot3 =
  302. [self readSnapshotForAggregate:[testCollection aggregate:@[
  303. [FIRAggregateField aggregateFieldForCount],
  304. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  305. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  306. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  307. ]]];
  308. // different data set
  309. FIRAggregateQuerySnapshot* snapshot4 = [self
  310. readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@50]
  311. aggregate:@[
  312. [FIRAggregateField aggregateFieldForCount],
  313. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  314. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  315. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  316. [FIRAggregateField
  317. aggregateFieldForAverageOfField:@"weight"]
  318. ]]];
  319. XCTAssertEqualObjects(snapshot1, snapshot2);
  320. XCTAssertNotEqualObjects(snapshot1, snapshot3);
  321. XCTAssertNotEqualObjects(snapshot1, snapshot4);
  322. XCTAssertNotEqualObjects(snapshot3, snapshot4);
  323. XCTAssertEqual([snapshot1 hash], [snapshot2 hash]);
  324. XCTAssertNotEqual([snapshot1 hash], [snapshot3 hash]);
  325. XCTAssertNotEqual([snapshot1 hash], [snapshot4 hash]);
  326. XCTAssertNotEqual([snapshot3 hash], [snapshot4 hash]);
  327. }
  328. #endif // #if !defined(THREAD_SANITIZER)
  329. - (void)testAllowsAliasesLongerThan1500Bytes {
  330. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  331. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  332. // The longest field name allowed is 1500. The alias chosen by the client is <op>_<fieldName>.
  333. // If the field name is
  334. // 1500 bytes, the alias will be longer than 1500, which is the limit for aliases. This is to
  335. // make sure the client
  336. // can handle this corner case correctly.
  337. NSString* longField = [@"" stringByPaddingToLength:1499
  338. withString:@"0123456789"
  339. startingAtIndex:0];
  340. FIRCollectionReference* testCollection =
  341. [self collectionRefWithDocuments:@{@"a" : @{longField : @1}, @"b" : @{longField : @2}}];
  342. FIRAggregateQuerySnapshot* snapshot =
  343. [self readSnapshotForAggregate:[testCollection
  344. aggregate:@[ [FIRAggregateField
  345. aggregateFieldForSumOfField:longField] ]]];
  346. // Sum
  347. XCTAssertEqual(
  348. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:longField]],
  349. [NSNumber numberWithLong:3], );
  350. }
  351. - (void)testCanGetDuplicateAggregations {
  352. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  353. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  354. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  355. @"a" : @{
  356. @"author" : @"authorA",
  357. @"title" : @"titleA",
  358. @"pages" : @100,
  359. @"height" : @24.5,
  360. @"weight" : @24.1,
  361. @"foo" : @1,
  362. @"bar" : @2,
  363. @"baz" : @3
  364. },
  365. @"b" : @{
  366. @"author" : @"authorB",
  367. @"title" : @"titleB",
  368. @"pages" : @50,
  369. @"height" : @25.5,
  370. @"weight" : @75.5,
  371. @"foo" : @1,
  372. @"bar" : @2,
  373. @"baz" : @3
  374. }
  375. }];
  376. FIRAggregateQuerySnapshot* snapshot =
  377. [self readSnapshotForAggregate:[testCollection aggregate:@[
  378. [FIRAggregateField aggregateFieldForCount],
  379. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  380. [FIRAggregateField aggregateFieldForSumOfField:@"pages"]
  381. ]]];
  382. // Count
  383. XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
  384. [NSNumber numberWithLong:2L]);
  385. // Sum
  386. XCTAssertEqual(
  387. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  388. [NSNumber numberWithLong:150L], );
  389. }
  390. - (void)testTerminateDoesNotCrashWithFlyingAggregateQuery {
  391. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  392. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  393. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  394. @"a" : @{
  395. @"author" : @"authorA",
  396. @"title" : @"titleA",
  397. @"pages" : @100,
  398. @"height" : @24.5,
  399. @"weight" : @24.1,
  400. @"foo" : @1,
  401. @"bar" : @2,
  402. @"baz" : @3
  403. },
  404. @"b" : @{
  405. @"author" : @"authorB",
  406. @"title" : @"titleB",
  407. @"pages" : @50,
  408. @"height" : @25.5,
  409. @"weight" : @75.5,
  410. @"foo" : @1,
  411. @"bar" : @2,
  412. @"baz" : @3
  413. }
  414. }];
  415. FIRAggregateQuery* aggQuery = [testCollection aggregate:@[
  416. [FIRAggregateField aggregateFieldForCount],
  417. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  418. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
  419. ]];
  420. __block FIRAggregateQuerySnapshot* result;
  421. XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
  422. [aggQuery aggregationWithSource:FIRAggregateSourceServer
  423. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  424. XCTAssertNil(error);
  425. result = snapshot;
  426. [expectation fulfill];
  427. }];
  428. [self awaitExpectation:expectation];
  429. // Count
  430. XCTAssertEqual([result valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
  431. [NSNumber numberWithLong:2L]);
  432. // Sum
  433. XCTAssertEqual(
  434. [result valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  435. [NSNumber numberWithLong:150L], );
  436. }
  437. - (void)testCanPerformMaxAggregations {
  438. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  439. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  440. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  441. @"a" : @{
  442. @"author" : @"authorA",
  443. @"title" : @"titleA",
  444. @"pages" : @100,
  445. @"height" : @24.5,
  446. @"weight" : @24.1,
  447. @"foo" : @1,
  448. @"bar" : @2,
  449. @"baz" : @3
  450. },
  451. @"b" : @{
  452. @"author" : @"authorB",
  453. @"title" : @"titleB",
  454. @"pages" : @50,
  455. @"height" : @25.5,
  456. @"weight" : @75.5,
  457. @"foo" : @1,
  458. @"bar" : @2,
  459. @"baz" : @3
  460. }
  461. }];
  462. // Max is 5, do not exceed
  463. FIRAggregateQuerySnapshot* snapshot =
  464. [self readSnapshotForAggregate:[testCollection aggregate:@[
  465. [FIRAggregateField aggregateFieldForCount],
  466. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  467. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  468. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  469. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
  470. ]]];
  471. // Assert
  472. XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
  473. [NSNumber numberWithLong:2L]);
  474. XCTAssertEqual(
  475. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  476. [NSNumber numberWithLong:150L], );
  477. XCTAssertEqual(
  478. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"weight"]]
  479. doubleValue],
  480. 99.6);
  481. XCTAssertEqual(
  482. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
  483. [NSNumber numberWithDouble:75.0]);
  484. XCTAssertEqual(
  485. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"weight"]]
  486. doubleValue],
  487. 49.8);
  488. }
  489. - (void)testCannotPerformMoreThanMaxAggregations {
  490. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  491. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  492. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  493. @"a" : @{
  494. @"author" : @"authorA",
  495. @"title" : @"titleA",
  496. @"pages" : @100,
  497. @"height" : @24.5,
  498. @"weight" : @24.1,
  499. @"foo" : @1,
  500. @"bar" : @2,
  501. @"baz" : @3
  502. },
  503. @"b" : @{
  504. @"author" : @"authorB",
  505. @"title" : @"titleB",
  506. @"pages" : @50,
  507. @"height" : @25.5,
  508. @"weight" : @75.5,
  509. @"foo" : @1,
  510. @"bar" : @2,
  511. @"baz" : @3
  512. }
  513. }];
  514. // Max is 5, we're attempting 6. I also like to live dangerously.
  515. FIRAggregateQuery* query = [testCollection aggregate:@[
  516. [FIRAggregateField aggregateFieldForCount],
  517. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  518. [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
  519. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  520. [FIRAggregateField aggregateFieldForAverageOfField:@"weight"],
  521. [FIRAggregateField aggregateFieldForAverageOfField:@"foo"]
  522. ]];
  523. __block NSError* result;
  524. XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
  525. [query aggregationWithSource:FIRAggregateSourceServer
  526. completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
  527. XCTAssertNil(snapshot);
  528. result = error;
  529. [expectation fulfill];
  530. }];
  531. [self awaitExpectation:expectation];
  532. XCTAssertNotNil(result);
  533. XCTAssertTrue([[result localizedDescription] containsString:@"maximum number of aggregations"]);
  534. }
  535. - (void)testCanRunAggregateCollectionGroupQuery {
  536. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  537. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  538. NSString* collectionGroup =
  539. [NSString stringWithFormat:@"%@%@", @"b",
  540. [self.db collectionWithPath:@"foo"].documentWithAutoID.documentID];
  541. NSArray* docPathFormats = @[
  542. @"abc/123/%@/cg-doc1", @"abc/123/%@/cg-doc2", @"%@/cg-doc3", @"%@/cg-doc4",
  543. @"def/456/%@/cg-doc5", @"%@/virtual-doc/nested-coll/not-cg-doc", @"x%@/not-cg-doc",
  544. @"%@x/not-cg-doc", @"abc/123/%@x/not-cg-doc", @"abc/123/x%@/not-cg-doc", @"abc/%@"
  545. ];
  546. FIRWriteBatch* batch = self.db.batch;
  547. for (NSString* format in docPathFormats) {
  548. NSString* path = [NSString stringWithFormat:format, collectionGroup];
  549. [batch setData:@{@"x" : @2} forDocument:[self.db documentWithPath:path]];
  550. }
  551. [self commitWriteBatch:batch];
  552. FIRAggregateQuerySnapshot* snapshot =
  553. [self readSnapshotForAggregate:[[self.db collectionGroupWithID:collectionGroup] aggregate:@[
  554. [FIRAggregateField aggregateFieldForCount],
  555. [FIRAggregateField aggregateFieldForSumOfField:@"x"],
  556. [FIRAggregateField aggregateFieldForAverageOfField:@"x"]
  557. ]]];
  558. // "cg-doc1", "cg-doc2", "cg-doc3", "cg-doc4", "cg-doc5",
  559. XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
  560. [NSNumber numberWithLong:5L]);
  561. XCTAssertEqual(
  562. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"x"]],
  563. [NSNumber numberWithLong:10L]);
  564. XCTAssertEqual(
  565. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"x"]],
  566. [NSNumber numberWithDouble:2.0]);
  567. }
  568. - (void)testPerformsAggregationsWhenNaNExistsForSomeFieldValues {
  569. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  570. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  571. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  572. @"a" : @{
  573. @"author" : @"authorA",
  574. @"title" : @"titleA",
  575. @"pages" : @100,
  576. @"year" : @1980,
  577. @"rating" : @5
  578. },
  579. @"b" : @{
  580. @"author" : @"authorB",
  581. @"title" : @"titleB",
  582. @"pages" : @50,
  583. @"year" : @2020,
  584. @"rating" : @4
  585. },
  586. @"c" : @{
  587. @"author" : @"authorC",
  588. @"title" : @"titleC",
  589. @"pages" : @100,
  590. @"year" : @1980,
  591. @"rating" : [NSNumber numberWithFloat:NAN]
  592. },
  593. @"d" : @{
  594. @"author" : @"authorD",
  595. @"title" : @"titleD",
  596. @"pages" : @50,
  597. @"year" : @2020,
  598. @"rating" : @0
  599. }
  600. }];
  601. FIRAggregateQuerySnapshot* snapshot =
  602. [self readSnapshotForAggregate:[testCollection aggregate:@[
  603. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  604. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  605. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  606. [FIRAggregateField aggregateFieldForAverageOfField:@"year"]
  607. ]]];
  608. // Sum
  609. XCTAssertEqual(
  610. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  611. [NSNumber numberWithDouble:NAN]);
  612. XCTAssertEqual(
  613. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
  614. longValue],
  615. 300L);
  616. // Average
  617. XCTAssertEqual(
  618. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
  619. [NSNumber numberWithDouble:NAN]);
  620. XCTAssertEqual(
  621. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"year"]]
  622. doubleValue],
  623. 2000.0);
  624. }
  625. - (void)testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation {
  626. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  627. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  628. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  629. @"a" : @{
  630. @"author" : @"authorA",
  631. @"title" : @"titleA",
  632. @"pages" : @100,
  633. @"height" : @24.5,
  634. @"weight" : @24.1,
  635. @"foo" : @1,
  636. @"bar" : @2,
  637. @"baz" : @3
  638. },
  639. @"b" : @{
  640. @"author" : @"authorB",
  641. @"title" : @"titleB",
  642. @"pages" : @50,
  643. @"height" : @25.5,
  644. @"weight" : @75.5,
  645. @"foo" : @1,
  646. @"bar" : @2,
  647. @"baz" : @3
  648. }
  649. }];
  650. FIRAggregateQuerySnapshot* snapshot =
  651. [self readSnapshotForAggregate:[testCollection
  652. aggregate:@[ [FIRAggregateField
  653. aggregateFieldForSumOfField:@"pages"] ]]];
  654. @try {
  655. [snapshot count];
  656. XCTAssertTrue(false, "Exception expected");
  657. } @catch (NSException* exception) {
  658. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  659. XCTAssertEqualObjects(exception.reason,
  660. @"'count()' was not requested in the aggregation query.");
  661. }
  662. @
  663. try {
  664. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
  665. XCTAssertTrue(false, "Exception expected");
  666. } @catch (NSException* exception) {
  667. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  668. XCTAssertEqualObjects(exception.reason,
  669. @"'sum(foo)' was not requested in the aggregation query.");
  670. }
  671. @
  672. try {
  673. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
  674. XCTAssertTrue(false, "Exception expected");
  675. } @catch (NSException* exception) {
  676. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  677. XCTAssertEqualObjects(exception.reason,
  678. @"'avg(pages)' was not requested in the aggregation query.");
  679. }
  680. }
  681. - (void)testPerformsAggregationWhenUsingInOperator {
  682. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  683. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  684. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  685. @"a" : @{
  686. @"author" : @"authorA",
  687. @"title" : @"titleA",
  688. @"pages" : @100,
  689. @"year" : @1980,
  690. @"rating" : @5
  691. },
  692. @"b" : @{
  693. @"author" : @"authorB",
  694. @"title" : @"titleB",
  695. @"pages" : @50,
  696. @"year" : @2020,
  697. @"rating" : @4
  698. },
  699. @"c" : @{
  700. @"author" : @"authorC",
  701. @"title" : @"titleC",
  702. @"pages" : @100,
  703. @"year" : @1980,
  704. @"rating" : @3
  705. },
  706. @"d" : @{
  707. @"author" : @"authorD",
  708. @"title" : @"titleD",
  709. @"pages" : @50,
  710. @"year" : @2020,
  711. @"rating" : @0
  712. }
  713. }];
  714. FIRAggregateQuerySnapshot* snapshot = [self
  715. readSnapshotForAggregate:[[testCollection queryWhereField:@"rating" in:@[ @5, @3 ]]
  716. aggregate:@[
  717. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  718. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  719. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  720. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  721. [FIRAggregateField aggregateFieldForCount]
  722. ]]];
  723. // Count
  724. XCTAssertEqual(
  725. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  726. // Sum
  727. XCTAssertEqual(
  728. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  729. longValue],
  730. 8L);
  731. XCTAssertEqual(
  732. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
  733. longValue],
  734. 200L);
  735. // Average
  736. XCTAssertEqual(
  737. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  738. doubleValue],
  739. 4.0);
  740. XCTAssertEqual(
  741. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]]
  742. doubleValue],
  743. 100.0);
  744. }
  745. - (void)testPerformsAggregationWhenUsingArrayContainsAnyOperator {
  746. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  747. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  748. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  749. @"a" : @{
  750. @"author" : @"authorA",
  751. @"title" : @"titleA",
  752. @"pages" : @100,
  753. @"year" : @1980,
  754. @"rating" : @[ @5, @1000 ]
  755. },
  756. @"b" : @{
  757. @"author" : @"authorB",
  758. @"title" : @"titleB",
  759. @"pages" : @50,
  760. @"year" : @2020,
  761. @"rating" : @[ @4 ]
  762. },
  763. @"c" : @{
  764. @"author" : @"authorC",
  765. @"title" : @"titleC",
  766. @"pages" : @100,
  767. @"year" : @1980,
  768. @"rating" : @[ @2222, @3 ]
  769. },
  770. @"d" : @{
  771. @"author" : @"authorD",
  772. @"title" : @"titleD",
  773. @"pages" : @50,
  774. @"year" : @2020,
  775. @"rating" : @[ @0 ]
  776. }
  777. }];
  778. FIRAggregateQuerySnapshot* snapshot = [self
  779. readSnapshotForAggregate:[[testCollection queryWhereField:@"rating"
  780. arrayContainsAny:@[ @5, @3 ]]
  781. aggregate:@[
  782. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  783. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  784. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  785. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  786. [FIRAggregateField aggregateFieldForCount]
  787. ]]];
  788. // Count
  789. XCTAssertEqual(
  790. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  791. // Sum
  792. XCTAssertEqual(
  793. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  794. longValue],
  795. 0L);
  796. XCTAssertEqual(
  797. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
  798. longValue],
  799. 200L);
  800. // Average
  801. XCTAssertEqualObjects(
  802. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
  803. [NSNull null]);
  804. XCTAssertEqual(
  805. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]]
  806. doubleValue],
  807. 100.0);
  808. }
  809. - (void)testPerformsAggregationsOnNestedMapValues {
  810. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  811. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  812. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  813. @"a" : @{
  814. @"author" : @"authorA",
  815. @"title" : @"titleA",
  816. @"metadata" : @{@"pages" : @100, @"rating" : @{@"critic" : @2, @"user" : @5}}
  817. },
  818. @"b" : @{
  819. @"author" : @"authorB",
  820. @"title" : @"titleB",
  821. @"metadata" : @{@"pages" : @50, @"rating" : @{@"critic" : @4, @"user" : @4}}
  822. },
  823. }];
  824. FIRAggregateQuerySnapshot* snapshot =
  825. [self readSnapshotForAggregate:[testCollection aggregate:@[
  826. [FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"],
  827. [FIRAggregateField aggregateFieldForSumOfField:@"metadata.rating.user"],
  828. [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"],
  829. [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.rating.critic"],
  830. [FIRAggregateField aggregateFieldForCount]
  831. ]]];
  832. // Count
  833. XCTAssertEqual(
  834. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  835. // Sum
  836. XCTAssertEqual(
  837. [[snapshot valueForAggregation:[FIRAggregateField
  838. aggregateFieldForSumOfField:@"metadata.pages"]] longValue],
  839. 150L);
  840. XCTAssertEqual(
  841. [[snapshot valueForAggregation:[FIRAggregateField
  842. aggregateFieldForSumOfField:@"metadata.rating.user"]]
  843. longValue],
  844. 9);
  845. // Average
  846. XCTAssertEqual(
  847. [[snapshot
  848. valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"]]
  849. doubleValue],
  850. 75.0);
  851. XCTAssertEqual(
  852. [[snapshot valueForAggregation:[FIRAggregateField
  853. aggregateFieldForAverageOfField:@"metadata.rating.critic"]]
  854. doubleValue],
  855. 3.0);
  856. }
  857. - (void)testPerformsSumThatOverflowsMaxLong {
  858. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  859. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  860. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  861. @"a" : @{
  862. @"author" : @"authorA",
  863. @"title" : @"titleA",
  864. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  865. },
  866. @"b" : @{
  867. @"author" : @"authorB",
  868. @"title" : @"titleB",
  869. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  870. },
  871. }];
  872. FIRAggregateQuerySnapshot* snapshot =
  873. [self readSnapshotForAggregate:[testCollection
  874. aggregate:@[ [FIRAggregateField
  875. aggregateFieldForSumOfField:@"rating"] ]]];
  876. // Sum
  877. XCTAssertEqual(
  878. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  879. doubleValue],
  880. [[NSNumber numberWithLong:LLONG_MAX] doubleValue] +
  881. [[NSNumber numberWithLong:LLONG_MAX] doubleValue]);
  882. }
  883. - (void)testPerformsSumThatCanOverflowLongValuesDuringAccumulation {
  884. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  885. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  886. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  887. @"a" : @{
  888. @"author" : @"authorA",
  889. @"title" : @"titleA",
  890. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  891. },
  892. @"b" : @{@"author" : @"authorB", @"title" : @"titleB", @"rating" : [NSNumber numberWithLong:1]},
  893. @"c" :
  894. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]}
  895. }];
  896. FIRAggregateQuerySnapshot* snapshot =
  897. [self readSnapshotForAggregate:[testCollection
  898. aggregate:@[ [FIRAggregateField
  899. aggregateFieldForSumOfField:@"rating"] ]]];
  900. // Sum
  901. XCTAssertEqual(
  902. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  903. longLongValue],
  904. [[NSNumber numberWithLong:LLONG_MAX - 100] longLongValue]);
  905. }
  906. - (void)testPerformsSumThatIsNegative {
  907. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  908. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  909. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  910. @"a" : @{
  911. @"author" : @"authorA",
  912. @"title" : @"titleA",
  913. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  914. },
  915. @"b" : @{
  916. @"author" : @"authorB",
  917. @"title" : @"titleB",
  918. @"rating" : [NSNumber numberWithLong:-LLONG_MAX]
  919. },
  920. @"c" :
  921. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]},
  922. @"d" : @{
  923. @"author" : @"authorD",
  924. @"title" : @"titleD",
  925. @"rating" : [NSNumber numberWithLong:-10000]
  926. }
  927. }];
  928. FIRAggregateQuerySnapshot* snapshot =
  929. [self readSnapshotForAggregate:[testCollection
  930. aggregate:@[ [FIRAggregateField
  931. aggregateFieldForSumOfField:@"rating"] ]]];
  932. // Sum
  933. XCTAssertEqual(
  934. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  935. longLongValue],
  936. [[NSNumber numberWithLong:-10101] longLongValue]);
  937. }
  938. - (void)testPerformsSumThatIsPositiveInfinity {
  939. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  940. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  941. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  942. @"a" : @{
  943. @"author" : @"authorA",
  944. @"title" : @"titleA",
  945. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  946. },
  947. @"b" : @{
  948. @"author" : @"authorB",
  949. @"title" : @"titleB",
  950. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  951. }
  952. }];
  953. FIRAggregateQuerySnapshot* snapshot =
  954. [self readSnapshotForAggregate:[testCollection
  955. aggregate:@[ [FIRAggregateField
  956. aggregateFieldForSumOfField:@"rating"] ]]];
  957. // Sum
  958. XCTAssertEqual(
  959. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  960. [NSNumber numberWithDouble:INFINITY]);
  961. }
  962. - (void)testPerformsSumThatIsNegativeInfinity {
  963. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  964. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  965. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  966. @"a" : @{
  967. @"author" : @"authorA",
  968. @"title" : @"titleA",
  969. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  970. },
  971. @"b" : @{
  972. @"author" : @"authorB",
  973. @"title" : @"titleB",
  974. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  975. }
  976. }];
  977. FIRAggregateQuerySnapshot* snapshot =
  978. [self readSnapshotForAggregate:[testCollection
  979. aggregate:@[ [FIRAggregateField
  980. aggregateFieldForSumOfField:@"rating"] ]]];
  981. // Sum
  982. XCTAssertEqual(
  983. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  984. [NSNumber numberWithDouble:-INFINITY]);
  985. }
  986. - (void)testPerformsSumThatIsValidButCouldOverflowDuringAggregation {
  987. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  988. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  989. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  990. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  991. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  992. @"c" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  993. @"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  994. @"e" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  995. @"f" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  996. @"g" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  997. @"h" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
  998. }];
  999. FIRAggregateQuerySnapshot* snapshot =
  1000. [self readSnapshotForAggregate:[testCollection
  1001. aggregate:@[ [FIRAggregateField
  1002. aggregateFieldForSumOfField:@"rating"] ]]];
  1003. // Sum
  1004. XCTAssertEqual(
  1005. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1006. longLongValue],
  1007. [[NSNumber numberWithLong:0] longLongValue]);
  1008. XCTAssertEqual(
  1009. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1010. doubleValue],
  1011. [[NSNumber numberWithLong:0] doubleValue]);
  1012. }
  1013. - (void)testPerformsSumOverResultSetOfZeroDocuments {
  1014. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1015. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1016. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1017. @"a" : @{
  1018. @"author" : @"authorA",
  1019. @"title" : @"titleA",
  1020. @"pages" : @100,
  1021. @"height" : @24.5,
  1022. @"weight" : @24.1,
  1023. @"foo" : @1,
  1024. @"bar" : @2,
  1025. @"baz" : @3
  1026. },
  1027. @"b" : @{
  1028. @"author" : @"authorB",
  1029. @"title" : @"titleB",
  1030. @"pages" : @50,
  1031. @"height" : @25.5,
  1032. @"weight" : @75.5,
  1033. @"foo" : @1,
  1034. @"bar" : @2,
  1035. @"baz" : @3
  1036. }
  1037. }];
  1038. FIRAggregateQuerySnapshot* snapshot =
  1039. [self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  1040. aggregate:@[ [FIRAggregateField
  1041. aggregateFieldForSumOfField:@"pages"] ]]];
  1042. // Sum
  1043. XCTAssertEqual(
  1044. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  1045. [NSNumber numberWithLong:0L]);
  1046. }
  1047. - (void)testPerformsSumOnlyOnNumericFields {
  1048. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1049. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1050. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1051. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  1052. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  1053. @"c" : @{@"rating" : @"3"},
  1054. @"d" : @{@"rating" : [NSNumber numberWithLong:1]}
  1055. }];
  1056. FIRAggregateQuerySnapshot* snapshot =
  1057. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1058. [FIRAggregateField aggregateFieldForCount],
  1059. [FIRAggregateField aggregateFieldForSumOfField:@"rating"]
  1060. ]]];
  1061. // Count
  1062. XCTAssertEqual(
  1063. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  1064. // Sum
  1065. XCTAssertEqual(
  1066. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1067. longLongValue],
  1068. [[NSNumber numberWithLong:10] longLongValue]);
  1069. }
  1070. - (void)testPerformsSumOfMinIEEE754 {
  1071. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1072. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1073. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1074. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  1075. }];
  1076. FIRAggregateQuerySnapshot* snapshot =
  1077. [self readSnapshotForAggregate:[testCollection
  1078. aggregate:@[ [FIRAggregateField
  1079. aggregateFieldForSumOfField:@"rating"] ]]];
  1080. // Sum
  1081. XCTAssertEqual(
  1082. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1083. doubleValue],
  1084. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  1085. }
  1086. - (void)testPerformsAverageOfVariousNumericTypes {
  1087. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1088. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1089. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1090. @"a" : @{
  1091. @"x" : @1,
  1092. @"intToInt" : [NSNumber numberWithLong:10],
  1093. @"floatToInt" : [NSNumber numberWithDouble:10.5],
  1094. @"mixedToInt" : [NSNumber numberWithLong:10],
  1095. @"floatToFloat" : [NSNumber numberWithDouble:5.5],
  1096. @"mixedToFloat" : [NSNumber numberWithDouble:8.6],
  1097. @"intToFloat" : [NSNumber numberWithLong:10]
  1098. },
  1099. @"b" : @{
  1100. @"intToInt" : [NSNumber numberWithLong:5],
  1101. @"floatToInt" : [NSNumber numberWithDouble:9.5],
  1102. @"mixedToInt" : [NSNumber numberWithDouble:9.5],
  1103. @"floatToFloat" : [NSNumber numberWithDouble:4.5],
  1104. @"mixedToFloat" : [NSNumber numberWithLong:9],
  1105. @"intToFloat" : [NSNumber numberWithLong:9]
  1106. },
  1107. @"c" : @{
  1108. @"intToInt" : [NSNumber numberWithLong:0],
  1109. @"floatToInt" : @"ignore",
  1110. @"mixedToInt" : [NSNumber numberWithDouble:10.5],
  1111. @"floatToFloat" : [NSNumber numberWithDouble:3.5],
  1112. @"mixedToFloat" : [NSNumber numberWithLong:10],
  1113. @"intToFloat" : @"ignore"
  1114. }
  1115. }];
  1116. NSArray* testCases = @[
  1117. @{
  1118. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToInt"],
  1119. @"expected" : [NSNumber numberWithLong:5]
  1120. },
  1121. @{
  1122. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToInt"],
  1123. @"expected" : [NSNumber numberWithLong:10]
  1124. },
  1125. @{
  1126. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToInt"],
  1127. @"expected" : [NSNumber numberWithLong:10]
  1128. },
  1129. @{
  1130. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToFloat"],
  1131. @"expected" : [NSNumber numberWithDouble:4.5]
  1132. },
  1133. @{
  1134. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToFloat"],
  1135. @"expected" : [NSNumber numberWithDouble:9.2]
  1136. },
  1137. @{
  1138. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToFloat"],
  1139. @"expected" : [NSNumber numberWithDouble:9.5]
  1140. }
  1141. ];
  1142. for (NSDictionary* testCase in testCases) {
  1143. FIRAggregateQuerySnapshot* snapshot =
  1144. [self readSnapshotForAggregate:[testCollection aggregate:@[ testCase[@"agg"] ]]];
  1145. // Average
  1146. XCTAssertEqual([[snapshot valueForAggregation:testCase[@"agg"]] longValue],
  1147. [testCase[@"expected"] longLongValue]);
  1148. XCTAssertEqualWithAccuracy([[snapshot valueForAggregation:testCase[@"agg"]] doubleValue],
  1149. [testCase[@"expected"] doubleValue], 0.00000000000001);
  1150. }
  1151. }
  1152. - (void)testPerformsAverageCausingUnderflow {
  1153. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1154. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1155. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1156. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]},
  1157. @"b" : @{@"rating" : [NSNumber numberWithDouble:0]}
  1158. }];
  1159. FIRAggregateQuerySnapshot* snapshot =
  1160. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1161. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1162. ]]];
  1163. // Average
  1164. XCTAssertEqual(
  1165. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1166. doubleValue],
  1167. [[NSNumber numberWithDouble:0] doubleValue]);
  1168. }
  1169. - (void)testPerformsAverageOfMinIEEE754 {
  1170. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1171. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1172. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1173. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  1174. }];
  1175. FIRAggregateQuerySnapshot* snapshot =
  1176. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1177. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1178. ]]];
  1179. // Average
  1180. XCTAssertEqual(
  1181. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1182. doubleValue],
  1183. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  1184. }
  1185. - (void)testPerformsAverageOverflowIEEE754DuringAccumulation {
  1186. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1187. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1188. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1189. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  1190. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
  1191. }];
  1192. FIRAggregateQuerySnapshot* snapshot =
  1193. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1194. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1195. ]]];
  1196. // Average
  1197. XCTAssertEqual(
  1198. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1199. doubleValue],
  1200. [[NSNumber numberWithDouble:INFINITY] doubleValue]);
  1201. }
  1202. - (void)testPerformsAverageOverResultSetOfZeroDocuments {
  1203. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1204. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1205. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1206. @"a" : @{
  1207. @"author" : @"authorA",
  1208. @"title" : @"titleA",
  1209. @"pages" : @100,
  1210. @"height" : @24.5,
  1211. @"weight" : @24.1,
  1212. @"foo" : @1,
  1213. @"bar" : @2,
  1214. @"baz" : @3
  1215. },
  1216. @"b" : @{
  1217. @"author" : @"authorB",
  1218. @"title" : @"titleB",
  1219. @"pages" : @50,
  1220. @"height" : @25.5,
  1221. @"weight" : @75.5,
  1222. @"foo" : @1,
  1223. @"bar" : @2,
  1224. @"baz" : @3
  1225. }
  1226. }];
  1227. FIRAggregateQuerySnapshot* snapshot = [self
  1228. readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  1229. aggregate:@[ [FIRAggregateField
  1230. aggregateFieldForAverageOfField:@"pages"] ]]];
  1231. // Average
  1232. XCTAssertEqual(
  1233. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
  1234. [NSNull null]);
  1235. }
  1236. - (void)testPerformsAverageOnlyOnNumericFields {
  1237. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1238. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1239. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1240. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  1241. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  1242. @"c" : @{@"rating" : @"3"},
  1243. @"d" : @{@"rating" : [NSNumber numberWithLong:6]}
  1244. }];
  1245. FIRAggregateQuerySnapshot* snapshot =
  1246. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1247. [FIRAggregateField aggregateFieldForCount],
  1248. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1249. ]]];
  1250. // Count
  1251. XCTAssertEqual(
  1252. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  1253. // Average
  1254. XCTAssertEqual(
  1255. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1256. doubleValue],
  1257. [[NSNumber numberWithDouble:5] doubleValue]);
  1258. }
  1259. @end