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)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:401
+[FlutterDartProject defaultBundleIdentifier]
NSString * defaultBundleIdentifier()
Definition: FlutterDartProject.mm:408
+[FlutterDartProject lookupKeyForAsset:]
NSString * lookupKeyForAsset:(NSString *asset)
Definition: FlutterDartProject.mm:388
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:397
-[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:262
+[FlutterDartProject lookupKeyForAsset:fromBundle:]
NSString * lookupKeyForAsset:fromBundle:(NSString *asset,[fromBundle] nullable NSBundle *bundle)
Definition: FlutterDartProject.mm:392
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13