| Flutter iOS Embedder |

FlutterDartProjectTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
7
8 #include "flutter/common/constants.h"
9 #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
10 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
11
12 FLUTTER_ASSERT_ARC
13
14 @interface FlutterDartProjectTest : XCTestCase
15 @end
16
17 @implementation FlutterDartProjectTest
18
19 - (void)setUp {
20 }
21
22 - (void)tearDown {
23 }
24
25 - (void)testOldGenHeapSizeSetting {
26 FlutterDartProject* project = [[FlutterDartProject alloc] init];
27 int64_t old_gen_heap_size =
28 std::round([NSProcessInfo processInfo].physicalMemory * .48 / flutter::kMegaByteSizeInBytes);
29 XCTAssertEqual(project.settings.old_gen_heap_size, old_gen_heap_size);
30 }
31
32 - (void)testResourceCacheMaxBytesThresholdSetting {
33 FlutterDartProject* project = [[FlutterDartProject alloc] init];
34 CGFloat scale = [UIScreen mainScreen].scale;
35 CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width * scale;
36 CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height * scale;
37 size_t resource_cache_max_bytes_threshold = screenWidth * screenHeight * 12 * 4;
38 XCTAssertEqual(project.settings.resource_cache_max_bytes_threshold,
39 resource_cache_max_bytes_threshold);
40 }
41
42 - (void)testMainBundleSettingsAreCorrectlyParsed {
43 NSBundle* mainBundle = [NSBundle mainBundle];
44 NSDictionary* appTransportSecurity =
45 [mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"];
46 XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:appTransportSecurity]);
47 XCTAssertEqualObjects(
48 @"[[\"invalid-site.com\",true,false],[\"sub.invalid-site.com\",false,false]]",
49 [FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
50 }
51
52 - (void)testLeakDartVMSettingsAreCorrectlyParsed {
53 // The FLTLeakDartVM's value is defined in Info.plist
54 NSBundle* mainBundle = [NSBundle mainBundle];
55 NSNumber* leakDartVM = [mainBundle objectForInfoDictionaryKey:@"FLTLeakDartVM"];
56 XCTAssertEqual(leakDartVM.boolValue, NO);
57
58 auto settings = FLTDefaultSettingsForBundle();
59 // Check settings.leak_vm value is same as the value defined in Info.plist.
60 XCTAssertEqual(settings.leak_vm, NO);
61 }
62
63 - (void)testFLTFrameworkBundleInternalWhenBundleIsNotPresent {
64 NSBundle* found =
65 FLTFrameworkBundleInternal(@"doesNotExist", NSBundle.mainBundle.privateFrameworksURL);
66 XCTAssertNil(found);
67 }
68
69 - (void)testFLTFrameworkBundleInternalWhenBundleIsPresent {
70 NSString* presentBundleID = @"io.flutter.flutter";
71 NSBundle* found =
72 FLTFrameworkBundleInternal(presentBundleID, NSBundle.mainBundle.privateFrameworksURL);
73 XCTAssertNotNil(found);
74 }
75
76 - (void)testFLTGetApplicationBundleWhenCurrentTargetIsNotExtension {
77 NSBundle* bundle = FLTGetApplicationBundle();
78 XCTAssertEqual(bundle, [NSBundle mainBundle]);
79 }
80
81 - (void)testFLTGetApplicationBundleWhenCurrentTargetIsExtension {
82 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
83 NSURL* url = [[NSBundle mainBundle].bundleURL URLByAppendingPathComponent:@"foo/ext.appex"];
84 OCMStub([mockMainBundle bundleURL]).andReturn(url);
85 NSBundle* bundle = FLTGetApplicationBundle();
86 [mockMainBundle stopMocking];
87 XCTAssertEqualObjects(bundle.bundleURL, [NSBundle mainBundle].bundleURL);
88 }
89
90 - (void)testFLTAssetsURLFromBundle {
91 {
92 // Found asset path in info.plist
93 id mockBundle = OCMClassMock([NSBundle class]);
94 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
95 NSString* resultAssetsPath = @"path/to/foo/assets";
96 OCMStub([mockBundle pathForResource:@"foo/assets" ofType:nil]).andReturn(resultAssetsPath);
97 NSString* path = FLTAssetsPathFromBundle(mockBundle);
98 XCTAssertEqualObjects(path, @"path/to/foo/assets");
99 }
100 {
101 // Found asset path in info.plist, is not overriden by main bundle
102 id mockBundle = OCMClassMock([NSBundle class]);
103 id mockMainBundle = OCMPartialMock(NSBundle.mainBundle);
104 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
105 OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil);
106 NSString* resultAssetsPath = @"path/to/foo/assets";
107 OCMStub([mockBundle pathForResource:@"foo/assets" ofType:nil]).andReturn(resultAssetsPath);
108 NSString* path = FLTAssetsPathFromBundle(mockBundle);
109 XCTAssertEqualObjects(path, @"path/to/foo/assets");
110 [mockMainBundle stopMocking];
111 }
112 {
113 // No asset path in info.plist, defaults to main bundle
114 id mockBundle = OCMClassMock([NSBundle class]);
115 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
116 NSString* resultAssetsPath = @"path/to/foo/assets";
117 OCMStub([mockBundle pathForResource:@"Frameworks/App.framework/flutter_assets" ofType:nil])
118 .andReturn(nil);
119 OCMStub([mockMainBundle pathForResource:@"Frameworks/App.framework/flutter_assets" ofType:nil])
120 .andReturn(resultAssetsPath);
121 NSString* path = FLTAssetsPathFromBundle(mockBundle);
122 XCTAssertEqualObjects(path, @"path/to/foo/assets");
123 [mockMainBundle stopMocking];
124 }
125 }
126
127 - (void)testFLTAssetPathReturnsTheCorrectValue {
128 {
129 // Found assets path in info.plist
130 id mockBundle = OCMClassMock([NSBundle class]);
131 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
132 XCTAssertEqualObjects(FLTAssetPath(mockBundle), @"foo/assets");
133 }
134 {
135 // No assets path in info.plist, use default value
136 id mockBundle = OCMClassMock([NSBundle class]);
137 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil);
138 XCTAssertEqualObjects(FLTAssetPath(mockBundle), kDefaultAssetPath);
139 }
140 }
141
142 - (void)testLookUpForAssets {
143 {
144 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
145 // Found assets path in info.plist
146 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
147 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar"];
148 // This is testing public API, changing this assert is likely to break plugins.
149 XCTAssertEqualObjects(assetsPath, @"foo/assets/bar");
150 [mockBundle stopMocking];
151 }
152 {
153 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
154 // No assets path in info.plist, use default value
155 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil);
156 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar"];
157 // This is testing public API, changing this assert is likely to break plugins.
158 XCTAssertEqualObjects(assetsPath, @"Frameworks/App.framework/flutter_assets/bar");
159 [mockBundle stopMocking];
160 }
161 }
162
163 - (void)testLookUpForAssetsFromBundle {
164 {
165 id mockBundle = OCMClassMock([NSBundle class]);
166 // Found assets path in info.plist
167 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
168 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromBundle:mockBundle];
169 // This is testing public API, changing this assert is likely to break plugins.
170 XCTAssertEqualObjects(assetsPath, @"foo/assets/bar");
171 }
172 {
173 // No assets path in info.plist, use default value
174 id mockBundle = OCMClassMock([NSBundle class]);
175 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil);
176 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromBundle:mockBundle];
177 // This is testing public API, changing this assert is likely to break plugins.
178 XCTAssertEqualObjects(assetsPath, @"Frameworks/App.framework/flutter_assets/bar");
179 }
180 }
181
182 - (void)testLookUpForAssetsFromPackage {
183 {
184 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
185 // Found assets path in info.plist
186 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
187 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromPackage:@"bar_package"];
188 // This is testing public API, changing this assert is likely to break plugins.
189 XCTAssertEqualObjects(assetsPath, @"foo/assets/packages/bar_package/bar");
190 [mockBundle stopMocking];
191 }
192 {
193 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
194 // No assets path in info.plist, use default value
195 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil);
196 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar" fromPackage:@"bar_package"];
197 // This is testing public API, changing this assert is likely to break plugins.
198 XCTAssertEqualObjects(assetsPath,
199 @"Frameworks/App.framework/flutter_assets/packages/bar_package/bar");
200 [mockBundle stopMocking];
201 }
202 }
203
204 - (void)testLookUpForAssetsFromPackageFromBundle {
205 {
206 id mockBundle = OCMClassMock([NSBundle class]);
207 // Found assets path in info.plist
208 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
209 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar"
210 fromPackage:@"bar_package"
211 fromBundle:mockBundle];
212 // This is testing public API, changing this assert is likely to break plugins.
213 XCTAssertEqualObjects(assetsPath, @"foo/assets/packages/bar_package/bar");
214 }
215 {
216 id mockBundle = OCMClassMock([NSBundle class]);
217 // No assets path in info.plist, use default value
218 OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(nil);
219 NSString* assetsPath = [FlutterDartProject lookupKeyForAsset:@"bar"
220 fromPackage:@"bar_package"
221 fromBundle:mockBundle];
222 // This is testing public API, changing this assert is likely to break plugins.
223 XCTAssertEqualObjects(assetsPath,
224 @"Frameworks/App.framework/flutter_assets/packages/bar_package/bar");
225 }
226 }
227
228 - (void)testDisableImpellerSettingIsCorrectlyParsed {
229 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
230 OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"NO");
231
232 auto settings = FLTDefaultSettingsForBundle();
233 // Check settings.enable_impeller value is same as the value defined in Info.plist.
234 XCTAssertEqual(settings.enable_impeller, NO);
235 [mockMainBundle stopMocking];
236 }
237
238 - (void)testRequestsWarningWhenImpellerOptOut {
239 auto settings = FLTDefaultSettingsForBundle();
240 XCTAssertEqual(settings.warn_on_impeller_opt_out, YES);
241 }
242
243 - (void)testEnableImpellerSettingIsCorrectlyParsed {
244 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
245 OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"YES");
246
247 auto settings = FLTDefaultSettingsForBundle();
248 // Check settings.enable_impeller value is same as the value defined in Info.plist.
249 XCTAssertEqual(settings.enable_impeller, YES);
250 [mockMainBundle stopMocking];
251 }
252
253 - (void)testEnableImpellerSettingIsCorrectlyOverriddenByCommandLine {
254 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
255 OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"NO");
256 id mockProcessInfo = OCMPartialMock([NSProcessInfo processInfo]);
257 NSArray* arguments = @[ @"process_name", @"--enable-impeller" ];
258 OCMStub([mockProcessInfo arguments]).andReturn(arguments);
259
260 auto settings = FLTDefaultSettingsForBundle(nil, mockProcessInfo);
261 // Check settings.enable_impeller value is same as the value on command line.
262 XCTAssertEqual(settings.enable_impeller, YES);
263 [mockMainBundle stopMocking];
264 }
265
266 - (void)testEnableDartAssertsCommandLineArgument {
267 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
268 OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableDartAsserts"]).andReturn(@"YES");
269 id mockProcessInfo = OCMPartialMock([NSProcessInfo processInfo]);
270 NSArray* arguments = @[ @"process_name" ];
271 OCMStub([mockProcessInfo arguments]).andReturn(arguments);
272
273 auto settings = FLTDefaultSettingsForBundle(nil, mockProcessInfo);
274
275 XCTAssertEqual(settings.dart_flags.size(), 1u);
276 XCTAssertEqual(settings.dart_flags[0], "--enable-asserts");
277 [mockMainBundle stopMocking];
278 }
279
280 - (void)testDisableImpellerSettingIsCorrectlyOverriddenByCommandLine {
281 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
282 OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"YES");
283 id mockProcessInfo = OCMPartialMock([NSProcessInfo processInfo]);
284 NSArray* arguments = @[ @"process_name", @"--enable-impeller=false" ];
285 OCMStub([mockProcessInfo arguments]).andReturn(arguments);
286
287 auto settings = FLTDefaultSettingsForBundle(nil, mockProcessInfo);
288 // Check settings.enable_impeller value is same as the value on command line.
289 XCTAssertEqual(settings.enable_impeller, NO);
290 [mockMainBundle stopMocking];
291 }
292
293 - (void)testDisableImpellerAppBundleSettingIsCorrectlyParsed {
294 NSString* bundleId = [FlutterDartProject defaultBundleIdentifier];
295 id mockAppBundle = OCMClassMock([NSBundle class]);
296 OCMStub([mockAppBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"NO");
297 OCMStub([mockAppBundle bundleWithIdentifier:bundleId]).andReturn(mockAppBundle);
298
299 auto settings = FLTDefaultSettingsForBundle();
300 // Check settings.enable_impeller value is same as the value defined in Info.plist.
301 XCTAssertEqual(settings.enable_impeller, NO);
302
303 [mockAppBundle stopMocking];
304 }
305
306 - (void)testEnableImpellerAppBundleSettingIsCorrectlyParsed {
307 NSString* bundleId = [FlutterDartProject defaultBundleIdentifier];
308 id mockAppBundle = OCMClassMock([NSBundle class]);
309 OCMStub([mockAppBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"YES");
310 OCMStub([mockAppBundle bundleWithIdentifier:bundleId]).andReturn(mockAppBundle);
311
312 // Since FLTEnableImpeller is set to false in the main bundle, this is also
313 // testing that setting FLTEnableImpeller in the app bundle takes
314 // precedence over setting it in the root bundle.
315
316 auto settings = FLTDefaultSettingsForBundle();
317 // Check settings.enable_impeller value is same as the value defined in Info.plist.
318 XCTAssertEqual(settings.enable_impeller, YES);
319
320 [mockAppBundle stopMocking];
321 }
322
323 - (void)testEnableTraceSystraceSettingIsCorrectlyParsed {
324 NSBundle* mainBundle = [NSBundle mainBundle];
325 NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTTraceSystrace"];
326 XCTAssertNotNil(enableTraceSystrace);
327 XCTAssertEqual(enableTraceSystrace.boolValue, NO);
328 auto settings = FLTDefaultSettingsForBundle();
329 XCTAssertEqual(settings.trace_systrace, NO);
330 }
331
332 - (void)testEnableDartProflingSettingIsCorrectlyParsed {
333 NSBundle* mainBundle = [NSBundle mainBundle];
334 NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTEnableDartProfiling"];
335 XCTAssertNotNil(enableTraceSystrace);
336 XCTAssertEqual(enableTraceSystrace.boolValue, NO);
337 auto settings = FLTDefaultSettingsForBundle();
338 XCTAssertEqual(settings.trace_systrace, NO);
339 }
340
341 - (void)testEmptySettingsAreCorrect {
342 XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:[[NSDictionary alloc] init]]);
343 XCTAssertEqualObjects(@"", [FlutterDartProject domainNetworkPolicy:[[NSDictionary alloc] init]]);
344 }
345
346 - (void)testAllowsArbitraryLoads {
347 XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @false}]);
348 XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @true}]);
349 }
350
351 - (void)testProperlyFormedExceptionDomains {
352 NSDictionary* domainInfoOne = @{
353 @"NSIncludesSubdomains" : @false,
354 @"NSExceptionAllowsInsecureHTTPLoads" : @true,
355 @"NSExceptionMinimumTLSVersion" : @"4.0"
356 };
357 NSDictionary* domainInfoTwo = @{
358 @"NSIncludesSubdomains" : @true,
359 @"NSExceptionAllowsInsecureHTTPLoads" : @false,
360 @"NSExceptionMinimumTLSVersion" : @"4.0"
361 };
362 NSDictionary* domainInfoThree = @{
363 @"NSIncludesSubdomains" : @false,
364 @"NSExceptionAllowsInsecureHTTPLoads" : @true,
365 @"NSExceptionMinimumTLSVersion" : @"4.0"
366 };
367 NSDictionary* exceptionDomains = @{
368 @"domain.name" : domainInfoOne,
369 @"sub.domain.name" : domainInfoTwo,
370 @"sub.two.domain.name" : domainInfoThree
371 };
372 NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
373 XCTAssertEqualObjects(@"[[\"domain.name\",false,true],[\"sub.domain.name\",true,false],"
374 @"[\"sub.two.domain.name\",false,true]]",
375 [FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
376 }
377
378 - (void)testExceptionDomainsWithMissingInfo {
379 NSDictionary* domainInfoOne = @{@"NSExceptionMinimumTLSVersion" : @"4.0"};
380 NSDictionary* domainInfoTwo = @{
381 @"NSIncludesSubdomains" : @true,
382 };
383 NSDictionary* domainInfoThree = @{};
384 NSDictionary* exceptionDomains = @{
385 @"domain.name" : domainInfoOne,
386 @"sub.domain.name" : domainInfoTwo,
387 @"sub.two.domain.name" : domainInfoThree
388 };
389 NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
390 XCTAssertEqualObjects(@"[[\"domain.name\",false,false],[\"sub.domain.name\",true,false],"
391 @"[\"sub.two.domain.name\",false,false]]",
392 [FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
393 }
394
395 @end
FLTFrameworkBundleInternal
NSBundle * FLTFrameworkBundleInternal(NSString *flutterFrameworkBundleID, NSURL *searchURL)
Definition: FlutterNSBundleUtils.mm:14
+[FlutterDartProject lookupKeyForAsset:fromPackage:fromBundle:]
NSString * lookupKeyForAsset:fromPackage:fromBundle:(NSString *asset,[fromPackage] NSString *package,[fromBundle] nullable NSBundle *bundle)
Definition: FlutterDartProject.mm:397
+[FlutterDartProject defaultBundleIdentifier]
NSString * defaultBundleIdentifier()
Definition: FlutterDartProject.mm:404
+[FlutterDartProject lookupKeyForAsset:]
NSString * lookupKeyForAsset:(NSString *asset)
Definition: FlutterDartProject.mm:384
FlutterDartProjectTest
Definition: FlutterDartProjectTest.mm:14
FlutterMacros.h
FLTAssetPath
NSString * FLTAssetPath(NSBundle *bundle)
Definition: FlutterNSBundleUtils.mm:57
kDefaultAssetPath
const NS_ASSUME_NONNULL_BEGIN NSString * kDefaultAssetPath
Definition: FlutterNSBundleUtils.mm:11
FLTGetApplicationBundle
NSBundle * FLTGetApplicationBundle()
Definition: FlutterNSBundleUtils.mm:32
FlutterDartProject_Internal.h
+[FlutterDartProject lookupKeyForAsset:fromPackage:]
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
Definition: FlutterDartProject.mm:393
-[FlutterDartProject settings]
const flutter::Settings & settings()
FLTDefaultSettingsForBundle
flutter::Settings FLTDefaultSettingsForBundle(NSBundle *bundle, NSProcessInfo *processInfoOrNil)
Definition: FlutterDartProject.mm:46
FLTAssetsPathFromBundle
NSString * FLTAssetsPathFromBundle(NSBundle *bundle)
Definition: FlutterNSBundleUtils.mm:61
FlutterDartProject
Definition: FlutterDartProject.mm:258
+[FlutterDartProject lookupKeyForAsset:fromBundle:]
NSString * lookupKeyForAsset:fromBundle:(NSString *asset,[fromBundle] nullable NSBundle *bundle)
Definition: FlutterDartProject.mm:388
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13