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"
11 
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)testDisableImpellerSettingIsCorrectlyOverriddenByCommandLine {
267  id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
268  OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"YES");
269  id mockProcessInfo = OCMPartialMock([NSProcessInfo processInfo]);
270  NSArray* arguments = @[ @"process_name", @"--enable-impeller=false" ];
271  OCMStub([mockProcessInfo arguments]).andReturn(arguments);
272 
273  auto settings = FLTDefaultSettingsForBundle(nil, mockProcessInfo);
274  // Check settings.enable_impeller value is same as the value on command line.
275  XCTAssertEqual(settings.enable_impeller, NO);
276  [mockMainBundle stopMocking];
277 }
278 
279 - (void)testDisableImpellerAppBundleSettingIsCorrectlyParsed {
280  NSString* bundleId = [FlutterDartProject defaultBundleIdentifier];
281  id mockAppBundle = OCMClassMock([NSBundle class]);
282  OCMStub([mockAppBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"NO");
283  OCMStub([mockAppBundle bundleWithIdentifier:bundleId]).andReturn(mockAppBundle);
284 
285  auto settings = FLTDefaultSettingsForBundle();
286  // Check settings.enable_impeller value is same as the value defined in Info.plist.
287  XCTAssertEqual(settings.enable_impeller, NO);
288 
289  [mockAppBundle stopMocking];
290 }
291 
292 - (void)testEnableImpellerAppBundleSettingIsCorrectlyParsed {
293  NSString* bundleId = [FlutterDartProject defaultBundleIdentifier];
294  id mockAppBundle = OCMClassMock([NSBundle class]);
295  OCMStub([mockAppBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"YES");
296  OCMStub([mockAppBundle bundleWithIdentifier:bundleId]).andReturn(mockAppBundle);
297 
298  // Since FLTEnableImpeller is set to false in the main bundle, this is also
299  // testing that setting FLTEnableImpeller in the app bundle takes
300  // precedence over setting it in the root bundle.
301 
302  auto settings = FLTDefaultSettingsForBundle();
303  // Check settings.enable_impeller value is same as the value defined in Info.plist.
304  XCTAssertEqual(settings.enable_impeller, YES);
305 
306  [mockAppBundle stopMocking];
307 }
308 
309 - (void)testEnableTraceSystraceSettingIsCorrectlyParsed {
310  NSBundle* mainBundle = [NSBundle mainBundle];
311  NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTTraceSystrace"];
312  XCTAssertNotNil(enableTraceSystrace);
313  XCTAssertEqual(enableTraceSystrace.boolValue, NO);
314  auto settings = FLTDefaultSettingsForBundle();
315  XCTAssertEqual(settings.trace_systrace, NO);
316 }
317 
318 - (void)testEnableDartProflingSettingIsCorrectlyParsed {
319  NSBundle* mainBundle = [NSBundle mainBundle];
320  NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTEnableDartProfiling"];
321  XCTAssertNotNil(enableTraceSystrace);
322  XCTAssertEqual(enableTraceSystrace.boolValue, NO);
323  auto settings = FLTDefaultSettingsForBundle();
324  XCTAssertEqual(settings.trace_systrace, NO);
325 }
326 
327 - (void)testEmptySettingsAreCorrect {
328  XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:[[NSDictionary alloc] init]]);
329  XCTAssertEqualObjects(@"", [FlutterDartProject domainNetworkPolicy:[[NSDictionary alloc] init]]);
330 }
331 
332 - (void)testAllowsArbitraryLoads {
333  XCTAssertFalse([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @false}]);
334  XCTAssertTrue([FlutterDartProject allowsArbitraryLoads:@{@"NSAllowsArbitraryLoads" : @true}]);
335 }
336 
337 - (void)testProperlyFormedExceptionDomains {
338  NSDictionary* domainInfoOne = @{
339  @"NSIncludesSubdomains" : @false,
340  @"NSExceptionAllowsInsecureHTTPLoads" : @true,
341  @"NSExceptionMinimumTLSVersion" : @"4.0"
342  };
343  NSDictionary* domainInfoTwo = @{
344  @"NSIncludesSubdomains" : @true,
345  @"NSExceptionAllowsInsecureHTTPLoads" : @false,
346  @"NSExceptionMinimumTLSVersion" : @"4.0"
347  };
348  NSDictionary* domainInfoThree = @{
349  @"NSIncludesSubdomains" : @false,
350  @"NSExceptionAllowsInsecureHTTPLoads" : @true,
351  @"NSExceptionMinimumTLSVersion" : @"4.0"
352  };
353  NSDictionary* exceptionDomains = @{
354  @"domain.name" : domainInfoOne,
355  @"sub.domain.name" : domainInfoTwo,
356  @"sub.two.domain.name" : domainInfoThree
357  };
358  NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
359  XCTAssertEqualObjects(@"[[\"domain.name\",false,true],[\"sub.domain.name\",true,false],"
360  @"[\"sub.two.domain.name\",false,true]]",
361  [FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
362 }
363 
364 - (void)testExceptionDomainsWithMissingInfo {
365  NSDictionary* domainInfoOne = @{@"NSExceptionMinimumTLSVersion" : @"4.0"};
366  NSDictionary* domainInfoTwo = @{
367  @"NSIncludesSubdomains" : @true,
368  };
369  NSDictionary* domainInfoThree = @{};
370  NSDictionary* exceptionDomains = @{
371  @"domain.name" : domainInfoOne,
372  @"sub.domain.name" : domainInfoTwo,
373  @"sub.two.domain.name" : domainInfoThree
374  };
375  NSDictionary* appTransportSecurity = @{@"NSExceptionDomains" : exceptionDomains};
376  XCTAssertEqualObjects(@"[[\"domain.name\",false,false],[\"sub.domain.name\",true,false],"
377  @"[\"sub.two.domain.name\",false,false]]",
378  [FlutterDartProject domainNetworkPolicy:appTransportSecurity]);
379 }
380 
381 @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:404
+[FlutterDartProject defaultBundleIdentifier]
NSString * defaultBundleIdentifier()
Definition: FlutterDartProject.mm:411
+[FlutterDartProject lookupKeyForAsset:]
NSString * lookupKeyForAsset:(NSString *asset)
Definition: FlutterDartProject.mm:391
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:400
-[FlutterDartProject settings]
const flutter::Settings & settings()
FLTDefaultSettingsForBundle
flutter::Settings FLTDefaultSettingsForBundle(NSBundle *bundle, NSProcessInfo *processInfoOrNil)
Definition: FlutterDartProject.mm:55
FLTAssetsPathFromBundle
NSString * FLTAssetsPathFromBundle(NSBundle *bundle)
Definition: FlutterNSBundleUtils.mm:61
FlutterDartProject
Definition: FlutterDartProject.mm:264
+[FlutterDartProject lookupKeyForAsset:fromBundle:]
NSString * lookupKeyForAsset:fromBundle:(NSString *asset,[fromBundle] nullable NSBundle *bundle)
Definition: FlutterDartProject.mm:395
FLUTTER_ASSERT_ARC
Definition: VsyncWaiterIosTest.mm:15