FIRAggregateTests.mm 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450
  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. XCTestExpectation* expectation = [self expectationWithDescription:@"commit"];
  552. [batch commitWithCompletion:^(NSError* error) {
  553. XCTAssertNil(error);
  554. [expectation fulfill];
  555. }];
  556. [self awaitExpectation:expectation];
  557. FIRAggregateQuerySnapshot* snapshot =
  558. [self readSnapshotForAggregate:[[self.db collectionGroupWithID:collectionGroup] aggregate:@[
  559. [FIRAggregateField aggregateFieldForCount],
  560. [FIRAggregateField aggregateFieldForSumOfField:@"x"],
  561. [FIRAggregateField aggregateFieldForAverageOfField:@"x"]
  562. ]]];
  563. // "cg-doc1", "cg-doc2", "cg-doc3", "cg-doc4", "cg-doc5",
  564. XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
  565. [NSNumber numberWithLong:5L]);
  566. XCTAssertEqual(
  567. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"x"]],
  568. [NSNumber numberWithLong:10L]);
  569. XCTAssertEqual(
  570. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"x"]],
  571. [NSNumber numberWithDouble:2.0]);
  572. }
  573. - (void)testPerformsAggregationsWhenNaNExistsForSomeFieldValues {
  574. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  575. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  576. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  577. @"a" : @{
  578. @"author" : @"authorA",
  579. @"title" : @"titleA",
  580. @"pages" : @100,
  581. @"year" : @1980,
  582. @"rating" : @5
  583. },
  584. @"b" : @{
  585. @"author" : @"authorB",
  586. @"title" : @"titleB",
  587. @"pages" : @50,
  588. @"year" : @2020,
  589. @"rating" : @4
  590. },
  591. @"c" : @{
  592. @"author" : @"authorC",
  593. @"title" : @"titleC",
  594. @"pages" : @100,
  595. @"year" : @1980,
  596. @"rating" : [NSNumber numberWithFloat:NAN]
  597. },
  598. @"d" : @{
  599. @"author" : @"authorD",
  600. @"title" : @"titleD",
  601. @"pages" : @50,
  602. @"year" : @2020,
  603. @"rating" : @0
  604. }
  605. }];
  606. FIRAggregateQuerySnapshot* snapshot =
  607. [self readSnapshotForAggregate:[testCollection aggregate:@[
  608. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  609. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  610. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  611. [FIRAggregateField aggregateFieldForAverageOfField:@"year"]
  612. ]]];
  613. // Sum
  614. XCTAssertEqual(
  615. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  616. [NSNumber numberWithDouble:NAN]);
  617. XCTAssertEqual(
  618. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
  619. longValue],
  620. 300L);
  621. // Average
  622. XCTAssertEqual(
  623. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
  624. [NSNumber numberWithDouble:NAN]);
  625. XCTAssertEqual(
  626. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"year"]]
  627. doubleValue],
  628. 2000.0);
  629. }
  630. - (void)testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation {
  631. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  632. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  633. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  634. @"a" : @{
  635. @"author" : @"authorA",
  636. @"title" : @"titleA",
  637. @"pages" : @100,
  638. @"height" : @24.5,
  639. @"weight" : @24.1,
  640. @"foo" : @1,
  641. @"bar" : @2,
  642. @"baz" : @3
  643. },
  644. @"b" : @{
  645. @"author" : @"authorB",
  646. @"title" : @"titleB",
  647. @"pages" : @50,
  648. @"height" : @25.5,
  649. @"weight" : @75.5,
  650. @"foo" : @1,
  651. @"bar" : @2,
  652. @"baz" : @3
  653. }
  654. }];
  655. FIRAggregateQuerySnapshot* snapshot =
  656. [self readSnapshotForAggregate:[testCollection
  657. aggregate:@[ [FIRAggregateField
  658. aggregateFieldForSumOfField:@"pages"] ]]];
  659. @try {
  660. [snapshot count];
  661. XCTAssertTrue(false, "Exception expected");
  662. } @catch (NSException* exception) {
  663. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  664. XCTAssertEqualObjects(exception.reason,
  665. @"'count()' was not requested in the aggregation query.");
  666. }
  667. @
  668. try {
  669. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
  670. XCTAssertTrue(false, "Exception expected");
  671. } @catch (NSException* exception) {
  672. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  673. XCTAssertEqualObjects(exception.reason,
  674. @"'sum(foo)' was not requested in the aggregation query.");
  675. }
  676. @
  677. try {
  678. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
  679. XCTAssertTrue(false, "Exception expected");
  680. } @catch (NSException* exception) {
  681. XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
  682. XCTAssertEqualObjects(exception.reason,
  683. @"'avg(pages)' was not requested in the aggregation query.");
  684. }
  685. }
  686. - (void)testPerformsAggregationWhenUsingInOperator {
  687. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  688. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  689. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  690. @"a" : @{
  691. @"author" : @"authorA",
  692. @"title" : @"titleA",
  693. @"pages" : @100,
  694. @"year" : @1980,
  695. @"rating" : @5
  696. },
  697. @"b" : @{
  698. @"author" : @"authorB",
  699. @"title" : @"titleB",
  700. @"pages" : @50,
  701. @"year" : @2020,
  702. @"rating" : @4
  703. },
  704. @"c" : @{
  705. @"author" : @"authorC",
  706. @"title" : @"titleC",
  707. @"pages" : @100,
  708. @"year" : @1980,
  709. @"rating" : @3
  710. },
  711. @"d" : @{
  712. @"author" : @"authorD",
  713. @"title" : @"titleD",
  714. @"pages" : @50,
  715. @"year" : @2020,
  716. @"rating" : @0
  717. }
  718. }];
  719. FIRAggregateQuerySnapshot* snapshot = [self
  720. readSnapshotForAggregate:[[testCollection queryWhereField:@"rating" in:@[ @5, @3 ]]
  721. aggregate:@[
  722. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  723. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  724. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  725. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  726. [FIRAggregateField aggregateFieldForCount]
  727. ]]];
  728. // Count
  729. XCTAssertEqual(
  730. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  731. // Sum
  732. XCTAssertEqual(
  733. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  734. longValue],
  735. 8L);
  736. XCTAssertEqual(
  737. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
  738. longValue],
  739. 200L);
  740. // Average
  741. XCTAssertEqual(
  742. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  743. doubleValue],
  744. 4.0);
  745. XCTAssertEqual(
  746. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]]
  747. doubleValue],
  748. 100.0);
  749. }
  750. - (void)testPerformsAggregationWhenUsingArrayContainsAnyOperator {
  751. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  752. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  753. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  754. @"a" : @{
  755. @"author" : @"authorA",
  756. @"title" : @"titleA",
  757. @"pages" : @100,
  758. @"year" : @1980,
  759. @"rating" : @[ @5, @1000 ]
  760. },
  761. @"b" : @{
  762. @"author" : @"authorB",
  763. @"title" : @"titleB",
  764. @"pages" : @50,
  765. @"year" : @2020,
  766. @"rating" : @[ @4 ]
  767. },
  768. @"c" : @{
  769. @"author" : @"authorC",
  770. @"title" : @"titleC",
  771. @"pages" : @100,
  772. @"year" : @1980,
  773. @"rating" : @[ @2222, @3 ]
  774. },
  775. @"d" : @{
  776. @"author" : @"authorD",
  777. @"title" : @"titleD",
  778. @"pages" : @50,
  779. @"year" : @2020,
  780. @"rating" : @[ @0 ]
  781. }
  782. }];
  783. FIRAggregateQuerySnapshot* snapshot = [self
  784. readSnapshotForAggregate:[[testCollection queryWhereField:@"rating"
  785. arrayContainsAny:@[ @5, @3 ]]
  786. aggregate:@[
  787. [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
  788. [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
  789. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
  790. [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
  791. [FIRAggregateField aggregateFieldForCount]
  792. ]]];
  793. // Count
  794. XCTAssertEqual(
  795. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  796. // Sum
  797. XCTAssertEqual(
  798. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  799. longValue],
  800. 0L);
  801. XCTAssertEqual(
  802. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
  803. longValue],
  804. 200L);
  805. // Average
  806. XCTAssertEqualObjects(
  807. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
  808. [NSNull null]);
  809. XCTAssertEqual(
  810. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]]
  811. doubleValue],
  812. 100.0);
  813. }
  814. - (void)testPerformsAggregationsOnNestedMapValues {
  815. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  816. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  817. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  818. @"a" : @{
  819. @"author" : @"authorA",
  820. @"title" : @"titleA",
  821. @"metadata" : @{@"pages" : @100, @"rating" : @{@"critic" : @2, @"user" : @5}}
  822. },
  823. @"b" : @{
  824. @"author" : @"authorB",
  825. @"title" : @"titleB",
  826. @"metadata" : @{@"pages" : @50, @"rating" : @{@"critic" : @4, @"user" : @4}}
  827. },
  828. }];
  829. FIRAggregateQuerySnapshot* snapshot =
  830. [self readSnapshotForAggregate:[testCollection aggregate:@[
  831. [FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"],
  832. [FIRAggregateField aggregateFieldForSumOfField:@"metadata.rating.user"],
  833. [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"],
  834. [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.rating.critic"],
  835. [FIRAggregateField aggregateFieldForCount]
  836. ]]];
  837. // Count
  838. XCTAssertEqual(
  839. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
  840. // Sum
  841. XCTAssertEqual(
  842. [[snapshot valueForAggregation:[FIRAggregateField
  843. aggregateFieldForSumOfField:@"metadata.pages"]] longValue],
  844. 150L);
  845. XCTAssertEqual(
  846. [[snapshot valueForAggregation:[FIRAggregateField
  847. aggregateFieldForSumOfField:@"metadata.rating.user"]]
  848. longValue],
  849. 9);
  850. // Average
  851. XCTAssertEqual(
  852. [[snapshot
  853. valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"]]
  854. doubleValue],
  855. 75.0);
  856. XCTAssertEqual(
  857. [[snapshot valueForAggregation:[FIRAggregateField
  858. aggregateFieldForAverageOfField:@"metadata.rating.critic"]]
  859. doubleValue],
  860. 3.0);
  861. }
  862. - (void)testPerformsSumThatOverflowsMaxLong {
  863. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  864. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  865. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  866. @"a" : @{
  867. @"author" : @"authorA",
  868. @"title" : @"titleA",
  869. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  870. },
  871. @"b" : @{
  872. @"author" : @"authorB",
  873. @"title" : @"titleB",
  874. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  875. },
  876. }];
  877. FIRAggregateQuerySnapshot* snapshot =
  878. [self readSnapshotForAggregate:[testCollection
  879. aggregate:@[ [FIRAggregateField
  880. aggregateFieldForSumOfField:@"rating"] ]]];
  881. // Sum
  882. XCTAssertEqual(
  883. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  884. doubleValue],
  885. [[NSNumber numberWithLong:LLONG_MAX] doubleValue] +
  886. [[NSNumber numberWithLong:LLONG_MAX] doubleValue]);
  887. }
  888. - (void)testPerformsSumThatCanOverflowLongValuesDuringAccumulation {
  889. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  890. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  891. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  892. @"a" : @{
  893. @"author" : @"authorA",
  894. @"title" : @"titleA",
  895. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  896. },
  897. @"b" : @{@"author" : @"authorB", @"title" : @"titleB", @"rating" : [NSNumber numberWithLong:1]},
  898. @"c" :
  899. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]}
  900. }];
  901. FIRAggregateQuerySnapshot* snapshot =
  902. [self readSnapshotForAggregate:[testCollection
  903. aggregate:@[ [FIRAggregateField
  904. aggregateFieldForSumOfField:@"rating"] ]]];
  905. // Sum
  906. XCTAssertEqual(
  907. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  908. longLongValue],
  909. [[NSNumber numberWithLong:LLONG_MAX - 100] longLongValue]);
  910. }
  911. - (void)testPerformsSumThatIsNegative {
  912. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  913. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  914. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  915. @"a" : @{
  916. @"author" : @"authorA",
  917. @"title" : @"titleA",
  918. @"rating" : [NSNumber numberWithLong:LLONG_MAX]
  919. },
  920. @"b" : @{
  921. @"author" : @"authorB",
  922. @"title" : @"titleB",
  923. @"rating" : [NSNumber numberWithLong:-LLONG_MAX]
  924. },
  925. @"c" :
  926. @{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]},
  927. @"d" : @{
  928. @"author" : @"authorD",
  929. @"title" : @"titleD",
  930. @"rating" : [NSNumber numberWithLong:-10000]
  931. }
  932. }];
  933. FIRAggregateQuerySnapshot* snapshot =
  934. [self readSnapshotForAggregate:[testCollection
  935. aggregate:@[ [FIRAggregateField
  936. aggregateFieldForSumOfField:@"rating"] ]]];
  937. // Sum
  938. XCTAssertEqual(
  939. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  940. longLongValue],
  941. [[NSNumber numberWithLong:-10101] longLongValue]);
  942. }
  943. - (void)testPerformsSumThatIsPositiveInfinity {
  944. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  945. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  946. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  947. @"a" : @{
  948. @"author" : @"authorA",
  949. @"title" : @"titleA",
  950. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  951. },
  952. @"b" : @{
  953. @"author" : @"authorB",
  954. @"title" : @"titleB",
  955. @"rating" : [NSNumber numberWithDouble:DBL_MAX]
  956. }
  957. }];
  958. FIRAggregateQuerySnapshot* snapshot =
  959. [self readSnapshotForAggregate:[testCollection
  960. aggregate:@[ [FIRAggregateField
  961. aggregateFieldForSumOfField:@"rating"] ]]];
  962. // Sum
  963. XCTAssertEqual(
  964. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  965. [NSNumber numberWithDouble:INFINITY]);
  966. }
  967. - (void)testPerformsSumThatIsNegativeInfinity {
  968. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  969. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  970. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  971. @"a" : @{
  972. @"author" : @"authorA",
  973. @"title" : @"titleA",
  974. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  975. },
  976. @"b" : @{
  977. @"author" : @"authorB",
  978. @"title" : @"titleB",
  979. @"rating" : [NSNumber numberWithDouble:-DBL_MAX]
  980. }
  981. }];
  982. FIRAggregateQuerySnapshot* snapshot =
  983. [self readSnapshotForAggregate:[testCollection
  984. aggregate:@[ [FIRAggregateField
  985. aggregateFieldForSumOfField:@"rating"] ]]];
  986. // Sum
  987. XCTAssertEqual(
  988. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
  989. [NSNumber numberWithDouble:-INFINITY]);
  990. }
  991. - (void)testPerformsSumThatIsValidButCouldOverflowDuringAggregation {
  992. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  993. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  994. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  995. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  996. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  997. @"c" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  998. @"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  999. @"e" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  1000. @"f" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  1001. @"g" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
  1002. @"h" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
  1003. }];
  1004. FIRAggregateQuerySnapshot* snapshot =
  1005. [self readSnapshotForAggregate:[testCollection
  1006. aggregate:@[ [FIRAggregateField
  1007. aggregateFieldForSumOfField:@"rating"] ]]];
  1008. // Sum
  1009. XCTAssertEqual(
  1010. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1011. longLongValue],
  1012. [[NSNumber numberWithLong:0] longLongValue]);
  1013. XCTAssertEqual(
  1014. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1015. doubleValue],
  1016. [[NSNumber numberWithLong:0] doubleValue]);
  1017. }
  1018. - (void)testPerformsSumOverResultSetOfZeroDocuments {
  1019. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1020. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1021. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1022. @"a" : @{
  1023. @"author" : @"authorA",
  1024. @"title" : @"titleA",
  1025. @"pages" : @100,
  1026. @"height" : @24.5,
  1027. @"weight" : @24.1,
  1028. @"foo" : @1,
  1029. @"bar" : @2,
  1030. @"baz" : @3
  1031. },
  1032. @"b" : @{
  1033. @"author" : @"authorB",
  1034. @"title" : @"titleB",
  1035. @"pages" : @50,
  1036. @"height" : @25.5,
  1037. @"weight" : @75.5,
  1038. @"foo" : @1,
  1039. @"bar" : @2,
  1040. @"baz" : @3
  1041. }
  1042. }];
  1043. FIRAggregateQuerySnapshot* snapshot =
  1044. [self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  1045. aggregate:@[ [FIRAggregateField
  1046. aggregateFieldForSumOfField:@"pages"] ]]];
  1047. // Sum
  1048. XCTAssertEqual(
  1049. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
  1050. [NSNumber numberWithLong:0L]);
  1051. }
  1052. - (void)testPerformsSumOnlyOnNumericFields {
  1053. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1054. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1055. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1056. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  1057. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  1058. @"c" : @{@"rating" : @"3"},
  1059. @"d" : @{@"rating" : [NSNumber numberWithLong:1]}
  1060. }];
  1061. FIRAggregateQuerySnapshot* snapshot =
  1062. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1063. [FIRAggregateField aggregateFieldForCount],
  1064. [FIRAggregateField aggregateFieldForSumOfField:@"rating"]
  1065. ]]];
  1066. // Count
  1067. XCTAssertEqual(
  1068. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  1069. // Sum
  1070. XCTAssertEqual(
  1071. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1072. longLongValue],
  1073. [[NSNumber numberWithLong:10] longLongValue]);
  1074. }
  1075. - (void)testPerformsSumOfMinIEEE754 {
  1076. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1077. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1078. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1079. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  1080. }];
  1081. FIRAggregateQuerySnapshot* snapshot =
  1082. [self readSnapshotForAggregate:[testCollection
  1083. aggregate:@[ [FIRAggregateField
  1084. aggregateFieldForSumOfField:@"rating"] ]]];
  1085. // Sum
  1086. XCTAssertEqual(
  1087. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
  1088. doubleValue],
  1089. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  1090. }
  1091. - (void)testPerformsAverageOfVariousNumericTypes {
  1092. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1093. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1094. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1095. @"a" : @{
  1096. @"x" : @1,
  1097. @"intToInt" : [NSNumber numberWithLong:10],
  1098. @"floatToInt" : [NSNumber numberWithDouble:10.5],
  1099. @"mixedToInt" : [NSNumber numberWithLong:10],
  1100. @"floatToFloat" : [NSNumber numberWithDouble:5.5],
  1101. @"mixedToFloat" : [NSNumber numberWithDouble:8.6],
  1102. @"intToFloat" : [NSNumber numberWithLong:10]
  1103. },
  1104. @"b" : @{
  1105. @"intToInt" : [NSNumber numberWithLong:5],
  1106. @"floatToInt" : [NSNumber numberWithDouble:9.5],
  1107. @"mixedToInt" : [NSNumber numberWithDouble:9.5],
  1108. @"floatToFloat" : [NSNumber numberWithDouble:4.5],
  1109. @"mixedToFloat" : [NSNumber numberWithLong:9],
  1110. @"intToFloat" : [NSNumber numberWithLong:9]
  1111. },
  1112. @"c" : @{
  1113. @"intToInt" : [NSNumber numberWithLong:0],
  1114. @"floatToInt" : @"ignore",
  1115. @"mixedToInt" : [NSNumber numberWithDouble:10.5],
  1116. @"floatToFloat" : [NSNumber numberWithDouble:3.5],
  1117. @"mixedToFloat" : [NSNumber numberWithLong:10],
  1118. @"intToFloat" : @"ignore"
  1119. }
  1120. }];
  1121. NSArray* testCases = @[
  1122. @{
  1123. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToInt"],
  1124. @"expected" : [NSNumber numberWithLong:5]
  1125. },
  1126. @{
  1127. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToInt"],
  1128. @"expected" : [NSNumber numberWithLong:10]
  1129. },
  1130. @{
  1131. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToInt"],
  1132. @"expected" : [NSNumber numberWithLong:10]
  1133. },
  1134. @{
  1135. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToFloat"],
  1136. @"expected" : [NSNumber numberWithDouble:4.5]
  1137. },
  1138. @{
  1139. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToFloat"],
  1140. @"expected" : [NSNumber numberWithDouble:9.2]
  1141. },
  1142. @{
  1143. @"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToFloat"],
  1144. @"expected" : [NSNumber numberWithDouble:9.5]
  1145. }
  1146. ];
  1147. for (NSDictionary* testCase in testCases) {
  1148. FIRAggregateQuerySnapshot* snapshot =
  1149. [self readSnapshotForAggregate:[testCollection aggregate:@[ testCase[@"agg"] ]]];
  1150. // Average
  1151. XCTAssertEqual([[snapshot valueForAggregation:testCase[@"agg"]] longValue],
  1152. [testCase[@"expected"] longLongValue]);
  1153. XCTAssertEqualWithAccuracy([[snapshot valueForAggregation:testCase[@"agg"]] doubleValue],
  1154. [testCase[@"expected"] doubleValue], 0.00000000000001);
  1155. }
  1156. }
  1157. - (void)testPerformsAverageCausingUnderflow {
  1158. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1159. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1160. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1161. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]},
  1162. @"b" : @{@"rating" : [NSNumber numberWithDouble:0]}
  1163. }];
  1164. FIRAggregateQuerySnapshot* snapshot =
  1165. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1166. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1167. ]]];
  1168. // Average
  1169. XCTAssertEqual(
  1170. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1171. doubleValue],
  1172. [[NSNumber numberWithDouble:0] doubleValue]);
  1173. }
  1174. - (void)testPerformsAverageOfMinIEEE754 {
  1175. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1176. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1177. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1178. @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
  1179. }];
  1180. FIRAggregateQuerySnapshot* snapshot =
  1181. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1182. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1183. ]]];
  1184. // Average
  1185. XCTAssertEqual(
  1186. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1187. doubleValue],
  1188. [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
  1189. }
  1190. - (void)testPerformsAverageOverflowIEEE754DuringAccumulation {
  1191. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1192. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1193. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1194. @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
  1195. @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
  1196. }];
  1197. FIRAggregateQuerySnapshot* snapshot =
  1198. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1199. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1200. ]]];
  1201. // Average
  1202. XCTAssertEqual(
  1203. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1204. doubleValue],
  1205. [[NSNumber numberWithDouble:INFINITY] doubleValue]);
  1206. }
  1207. - (void)testPerformsAverageOverResultSetOfZeroDocuments {
  1208. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1209. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1210. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1211. @"a" : @{
  1212. @"author" : @"authorA",
  1213. @"title" : @"titleA",
  1214. @"pages" : @100,
  1215. @"height" : @24.5,
  1216. @"weight" : @24.1,
  1217. @"foo" : @1,
  1218. @"bar" : @2,
  1219. @"baz" : @3
  1220. },
  1221. @"b" : @{
  1222. @"author" : @"authorB",
  1223. @"title" : @"titleB",
  1224. @"pages" : @50,
  1225. @"height" : @25.5,
  1226. @"weight" : @75.5,
  1227. @"foo" : @1,
  1228. @"bar" : @2,
  1229. @"baz" : @3
  1230. }
  1231. }];
  1232. FIRAggregateQuerySnapshot* snapshot = [self
  1233. readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
  1234. aggregate:@[ [FIRAggregateField
  1235. aggregateFieldForAverageOfField:@"pages"] ]]];
  1236. // Average
  1237. XCTAssertEqual(
  1238. [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
  1239. [NSNull null]);
  1240. }
  1241. - (void)testPerformsAverageOnlyOnNumericFields {
  1242. // TODO(sum/avg) remove the check below when sum and avg are supported in production
  1243. XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
  1244. FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
  1245. @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
  1246. @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
  1247. @"c" : @{@"rating" : @"3"},
  1248. @"d" : @{@"rating" : [NSNumber numberWithLong:6]}
  1249. }];
  1250. FIRAggregateQuerySnapshot* snapshot =
  1251. [self readSnapshotForAggregate:[testCollection aggregate:@[
  1252. [FIRAggregateField aggregateFieldForCount],
  1253. [FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
  1254. ]]];
  1255. // Count
  1256. XCTAssertEqual(
  1257. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
  1258. // Average
  1259. XCTAssertEqual(
  1260. [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
  1261. doubleValue],
  1262. [[NSNumber numberWithDouble:5] doubleValue]);
  1263. }
  1264. @end