Merge pull request #150 from facebook/simulatorkit-framebuffer
Boot Simulator Directly and connect to Framebuffer
This commit is contained in:
Коммит
0f86251b43
|
@ -1,8 +1,9 @@
|
|||
language: objective-c
|
||||
osx_image: xcode7.2
|
||||
env:
|
||||
- MODE=framework FBSIMULATORCONTROL_DEVICE_SET=default
|
||||
- MODE=framework FBSIMULATORCONTROL_DEVICE_SET=custom
|
||||
- MODE=framework FBSIMULATORCONTROL_DEVICE_SET=default FBSIMULATORCONTROL_LAUNCH_TYPE=simulator_app
|
||||
- MODE=framework FBSIMULATORCONTROL_DEVICE_SET=custom FBSIMULATORCONTROL_LAUNCH_TYPE=simulator_app
|
||||
- MODE=framework FBSIMULATORCONTROL_DEVICE_SET=custom FBSIMULATORCONTROL_LAUNCH_TYPE=direct
|
||||
- MODE=cli
|
||||
- MODE=cli_framework
|
||||
script: ./build.sh
|
||||
|
|
|
@ -23,6 +23,21 @@
|
|||
AA2219951C3E752800371B01 /* FBCoreSimulatorTerminationStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = AA2219931C3E752800371B01 /* FBCoreSimulatorTerminationStrategy.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA2219961C3E752800371B01 /* FBCoreSimulatorTerminationStrategy.m in Sources */ = {isa = PBXBuildFile; fileRef = AA2219941C3E752800371B01 /* FBCoreSimulatorTerminationStrategy.m */; };
|
||||
AA3230CB1BDA387700C5BA01 /* FBSimulatorControlAssertions.m in Sources */ = {isa = PBXBuildFile; fileRef = AA3230CA1BDA387700C5BA01 /* FBSimulatorControlAssertions.m */; };
|
||||
AA4242EA1C528338008ABD80 /* FBFramebufferDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242DF1C528338008ABD80 /* FBFramebufferDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4242ED1C528338008ABD80 /* FBSimulatorFramebuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242E21C528338008ABD80 /* FBSimulatorFramebuffer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4242EE1C528338008ABD80 /* FBSimulatorFramebuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = AA4242E31C528338008ABD80 /* FBSimulatorFramebuffer.m */; };
|
||||
AA4242F11C529145008ABD80 /* FBFramebufferCompositeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242EF1C529145008ABD80 /* FBFramebufferCompositeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4242F21C529145008ABD80 /* FBFramebufferCompositeDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AA4242F01C529145008ABD80 /* FBFramebufferCompositeDelegate.m */; };
|
||||
AA4242F51C529213008ABD80 /* FBFramebufferCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242F31C529213008ABD80 /* FBFramebufferCounter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4242F61C529213008ABD80 /* FBFramebufferCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = AA4242F41C529213008ABD80 /* FBFramebufferCounter.m */; };
|
||||
AA4242F91C529280008ABD80 /* FBFramebufferDebugWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242F71C52927F008ABD80 /* FBFramebufferDebugWindow.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4242FA1C529280008ABD80 /* FBFramebufferDebugWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = AA4242F81C529280008ABD80 /* FBFramebufferDebugWindow.m */; };
|
||||
AA4242FD1C529366008ABD80 /* FBFramebufferVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242FB1C529366008ABD80 /* FBFramebufferVideo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4242FE1C529366008ABD80 /* FBFramebufferVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = AA4242FC1C529366008ABD80 /* FBFramebufferVideo.m */; };
|
||||
AA4243011C529393008ABD80 /* FBCapacityQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = AA4242FF1C529393008ABD80 /* FBCapacityQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA4243021C529393008ABD80 /* FBCapacityQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = AA4243001C529393008ABD80 /* FBCapacityQueue.m */; };
|
||||
AA4243041C5295FC008ABD80 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA4243031C5295FC008ABD80 /* CoreMedia.framework */; };
|
||||
AA4243061C529644008ABD80 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA4243051C529644008ABD80 /* CoreVideo.framework */; };
|
||||
AA5639551C060005009BAFAA /* FBSimulatorControl.h in Headers */ = {isa = PBXBuildFile; fileRef = AA5639541C05FFF5009BAFAA /* FBSimulatorControl.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA6424FE1C44E99B00AA9BFB /* FBSimulatorLaunchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AA6424FD1C44E99B00AA9BFB /* FBSimulatorLaunchTests.m */; };
|
||||
AA7490051C4E6CBA00F3BDBA /* FBSimulatorLaunchConfiguration+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AA7490041C4E6C7700F3BDBA /* FBSimulatorLaunchConfiguration+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -142,6 +157,8 @@
|
|||
AAB207C11C2099A9007C7908 /* FBSimulatorLoggingEventSink.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB207BF1C2099A9007C7908 /* FBSimulatorLoggingEventSink.m */; };
|
||||
AAB4AC1E1BB586930046F6A1 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAB4AC1D1BB586930046F6A1 /* AVFoundation.framework */; };
|
||||
AAB4AC271BBBC6880046F6A1 /* FBSimulatorControlTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB4AC261BBBC6880046F6A1 /* FBSimulatorControlTestCase.m */; };
|
||||
AABD8DF91C592DBA008527CD /* FBFramebufferImage.h in Headers */ = {isa = PBXBuildFile; fileRef = AABD8DF71C592DBA008527CD /* FBFramebufferImage.h */; };
|
||||
AABD8DFA1C592DBA008527CD /* FBFramebufferImage.m in Sources */ = {isa = PBXBuildFile; fileRef = AABD8DF81C592DBA008527CD /* FBFramebufferImage.m */; };
|
||||
AAC083761B9FB89600451648 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DD70E29A6018C7A00000000 /* CoreGraphics.framework */; };
|
||||
AAC083781B9FBA7600451648 /* FBSimulatorControl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1DD70E291A4B50E500000001 /* FBSimulatorControl.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
AAC083791B9FBACB00451648 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DD70E2976B173B900000000 /* Cocoa.framework */; };
|
||||
|
@ -257,6 +274,21 @@
|
|||
AA2DDC391C284044000689C6 /* SimVerifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimVerifier.h; sourceTree = "<group>"; };
|
||||
AA3230C91BDA387700C5BA01 /* FBSimulatorControlAssertions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSimulatorControlAssertions.h; sourceTree = "<group>"; };
|
||||
AA3230CA1BDA387700C5BA01 /* FBSimulatorControlAssertions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSimulatorControlAssertions.m; sourceTree = "<group>"; };
|
||||
AA4242DF1C528338008ABD80 /* FBFramebufferDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFramebufferDelegate.h; sourceTree = "<group>"; };
|
||||
AA4242E21C528338008ABD80 /* FBSimulatorFramebuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSimulatorFramebuffer.h; sourceTree = "<group>"; };
|
||||
AA4242E31C528338008ABD80 /* FBSimulatorFramebuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSimulatorFramebuffer.m; sourceTree = "<group>"; };
|
||||
AA4242EF1C529145008ABD80 /* FBFramebufferCompositeDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFramebufferCompositeDelegate.h; sourceTree = "<group>"; };
|
||||
AA4242F01C529145008ABD80 /* FBFramebufferCompositeDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFramebufferCompositeDelegate.m; sourceTree = "<group>"; };
|
||||
AA4242F31C529213008ABD80 /* FBFramebufferCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFramebufferCounter.h; sourceTree = "<group>"; };
|
||||
AA4242F41C529213008ABD80 /* FBFramebufferCounter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFramebufferCounter.m; sourceTree = "<group>"; };
|
||||
AA4242F71C52927F008ABD80 /* FBFramebufferDebugWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFramebufferDebugWindow.h; sourceTree = "<group>"; };
|
||||
AA4242F81C529280008ABD80 /* FBFramebufferDebugWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFramebufferDebugWindow.m; sourceTree = "<group>"; };
|
||||
AA4242FB1C529366008ABD80 /* FBFramebufferVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFramebufferVideo.h; sourceTree = "<group>"; };
|
||||
AA4242FC1C529366008ABD80 /* FBFramebufferVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFramebufferVideo.m; sourceTree = "<group>"; };
|
||||
AA4242FF1C529393008ABD80 /* FBCapacityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBCapacityQueue.h; sourceTree = "<group>"; };
|
||||
AA4243001C529393008ABD80 /* FBCapacityQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBCapacityQueue.m; sourceTree = "<group>"; };
|
||||
AA4243031C5295FC008ABD80 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
|
||||
AA4243051C529644008ABD80 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
|
||||
AA4876491BAC7399007F7D23 /* FBSimulatorControl-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "FBSimulatorControl-Info.plist"; sourceTree = "<group>"; };
|
||||
AA48776C1BAC74DC007F7D23 /* _DVTAsynchronousRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = _DVTAsynchronousRequest.h; sourceTree = "<group>"; };
|
||||
AA48776D1BAC74DC007F7D23 /* _DVTCancellationBlockToken.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = _DVTCancellationBlockToken.h; sourceTree = "<group>"; };
|
||||
|
@ -934,6 +966,8 @@
|
|||
AAB4AC1D1BB586930046F6A1 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
AAB4AC251BBBC6880046F6A1 /* FBSimulatorControlTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSimulatorControlTestCase.h; sourceTree = "<group>"; };
|
||||
AAB4AC261BBBC6880046F6A1 /* FBSimulatorControlTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSimulatorControlTestCase.m; sourceTree = "<group>"; };
|
||||
AABD8DF71C592DBA008527CD /* FBFramebufferImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFramebufferImage.h; sourceTree = "<group>"; };
|
||||
AABD8DF81C592DBA008527CD /* FBFramebufferImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFramebufferImage.m; sourceTree = "<group>"; };
|
||||
AAC241231BB3113F0054570C /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
AAC241251BB311690054570C /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; };
|
||||
AACA2C351C2976B100979C45 /* FBAddVideoPolyfill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBAddVideoPolyfill.h; sourceTree = "<group>"; };
|
||||
|
@ -982,6 +1016,8 @@
|
|||
AAC241241BB3113F0054570C /* AppKit.framework in Frameworks */,
|
||||
E7A30F0476B173B900000000 /* Cocoa.framework in Frameworks */,
|
||||
E7A30F04A6018C7A00000000 /* CoreGraphics.framework in Frameworks */,
|
||||
AA4243041C5295FC008ABD80 /* CoreMedia.framework in Frameworks */,
|
||||
AA4243061C529644008ABD80 /* CoreVideo.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1028,6 +1064,26 @@
|
|||
path = SimulatorKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AA4242D81C528338008ABD80 /* Framebuffer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AA4242EF1C529145008ABD80 /* FBFramebufferCompositeDelegate.h */,
|
||||
AA4242F01C529145008ABD80 /* FBFramebufferCompositeDelegate.m */,
|
||||
AA4242F31C529213008ABD80 /* FBFramebufferCounter.h */,
|
||||
AA4242F41C529213008ABD80 /* FBFramebufferCounter.m */,
|
||||
AA4242F71C52927F008ABD80 /* FBFramebufferDebugWindow.h */,
|
||||
AA4242F81C529280008ABD80 /* FBFramebufferDebugWindow.m */,
|
||||
AA4242DF1C528338008ABD80 /* FBFramebufferDelegate.h */,
|
||||
AABD8DF71C592DBA008527CD /* FBFramebufferImage.h */,
|
||||
AABD8DF81C592DBA008527CD /* FBFramebufferImage.m */,
|
||||
AA4242FB1C529366008ABD80 /* FBFramebufferVideo.h */,
|
||||
AA4242FC1C529366008ABD80 /* FBFramebufferVideo.m */,
|
||||
AA4242E21C528338008ABD80 /* FBSimulatorFramebuffer.h */,
|
||||
AA4242E31C528338008ABD80 /* FBSimulatorFramebuffer.m */,
|
||||
);
|
||||
path = Framebuffer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AA48775A1BAC74DC007F7D23 /* PrivateHeaders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1670,6 +1726,7 @@
|
|||
AAA46E431C0CB92A009D6452 /* FBSimulatorControl.xcconfig */,
|
||||
AA9516C11C15F54600A89CAD /* Configuration */,
|
||||
AA9516D21C15F54600A89CAD /* Events */,
|
||||
AA4242D81C528338008ABD80 /* Framebuffer */,
|
||||
AA9516DE1C15F54600A89CAD /* Interactions */,
|
||||
AA9516F31C15F54600A89CAD /* Logs */,
|
||||
AA9516FA1C15F54600A89CAD /* Management */,
|
||||
|
@ -1904,6 +1961,8 @@
|
|||
AACA2C361C2976B100979C45 /* FBAddVideoPolyfill.m */,
|
||||
AA0771EF1C1ADFA300E7FD52 /* FBBinaryParser.h */,
|
||||
AA0771F01C1ADFA300E7FD52 /* FBBinaryParser.m */,
|
||||
AA4242FF1C529393008ABD80 /* FBCapacityQueue.h */,
|
||||
AA4243001C529393008ABD80 /* FBCapacityQueue.m */,
|
||||
AA1D653C1C21A9690069F90D /* FBCollectionDescriptions.h */,
|
||||
AA1D653D1C21A9690069F90D /* FBCollectionDescriptions.m */,
|
||||
AA95173A1C15F54600A89CAD /* FBConcurrentCollectionOperations.h */,
|
||||
|
@ -1947,12 +2006,14 @@
|
|||
B401C97968022A5500000000 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AAB4AC1D1BB586930046F6A1 /* AVFoundation.framework */,
|
||||
AAC241251BB311690054570C /* ApplicationServices.framework */,
|
||||
AAC241231BB3113F0054570C /* AppKit.framework */,
|
||||
AAC241251BB311690054570C /* ApplicationServices.framework */,
|
||||
AAB4AC1D1BB586930046F6A1 /* AVFoundation.framework */,
|
||||
1DD70E2976B173B900000000 /* Cocoa.framework */,
|
||||
1DD70E29A6018C7A00000000 /* CoreGraphics.framework */,
|
||||
AA4243031C5295FC008ABD80 /* CoreMedia.framework */,
|
||||
1DD70E29B6970A5500000000 /* CoreSimulator.framework */,
|
||||
AA4243051C529644008ABD80 /* CoreVideo.framework */,
|
||||
1DD70E29B67B6FA500000000 /* DVTFoundation.framework */,
|
||||
1DD70E291AC72D1E00000000 /* DVTiPhoneSimulatorRemoteClient.framework */,
|
||||
1DD70E291A4B50E500000000 /* FBSimulatorControl.framework */,
|
||||
|
@ -2006,6 +2067,7 @@
|
|||
AA5639551C060005009BAFAA /* FBSimulatorControl.h in Headers */,
|
||||
AA9517A41C15F54600A89CAD /* FBTask+Private.h in Headers */,
|
||||
AA9517571C15F54600A89CAD /* FBSimulatorResourceManager.h in Headers */,
|
||||
AA4242FD1C529366008ABD80 /* FBFramebufferVideo.h in Headers */,
|
||||
AA95174A1C15F54600A89CAD /* FBProcessLaunchConfiguration.h in Headers */,
|
||||
AA9517A91C15F54600A89CAD /* FBTaskExecutor+Private.h in Headers */,
|
||||
AAD51E9F1C3ADECA00A763D0 /* FBSimulatorLaunchConfiguration.h in Headers */,
|
||||
|
@ -2021,6 +2083,7 @@
|
|||
AAF8DA651C1AFF81003B519E /* FBProcessInfo+Helpers.h in Headers */,
|
||||
AA95177C1C15F54600A89CAD /* FBSimulator+Helpers.h in Headers */,
|
||||
AA9517B41C15F54600A89CAD /* FBConcurrentCollectionOperations.h in Headers */,
|
||||
AA4242F51C529213008ABD80 /* FBFramebufferCounter.h in Headers */,
|
||||
AA9517AE1C15F54600A89CAD /* FBSimulatorWindowHelpers.h in Headers */,
|
||||
AA95176D1C15F54600A89CAD /* FBSimulatorInteraction+Private.h in Headers */,
|
||||
AAF8DA6D1C1AFFF0003B519E /* FBProcessQuery+Helpers.h in Headers */,
|
||||
|
@ -2031,6 +2094,8 @@
|
|||
AAD497871C50F0FD00ABC1A7 /* FBDebugDescribeable.h in Headers */,
|
||||
AA9517531C15F54600A89CAD /* FBSimulatorControlConfiguration.h in Headers */,
|
||||
AA1D653E1C21A9690069F90D /* FBCollectionDescriptions.h in Headers */,
|
||||
AA4242F11C529145008ABD80 /* FBFramebufferCompositeDelegate.h in Headers */,
|
||||
AA4242ED1C528338008ABD80 /* FBSimulatorFramebuffer.h in Headers */,
|
||||
AA95177F1C15F54600A89CAD /* FBSimulator.h in Headers */,
|
||||
AA9517551C15F54600A89CAD /* FBSimulatorControlGlobalConfiguration.h in Headers */,
|
||||
AA9517B61C15F54600A89CAD /* FBSimDeviceWrapper.h in Headers */,
|
||||
|
@ -2047,10 +2112,12 @@
|
|||
AA9517AC1C15F54600A89CAD /* FBTerminationHandle.h in Headers */,
|
||||
AAF8DA691C1AFFB1003B519E /* FBProcessInfo.h in Headers */,
|
||||
AA9517911C15F54600A89CAD /* FBSimulatorHistory+Queries.h in Headers */,
|
||||
AABD8DF91C592DBA008527CD /* FBFramebufferImage.h in Headers */,
|
||||
AA9517631C15F54600A89CAD /* FBInteraction.h in Headers */,
|
||||
AA9517841C15F54600A89CAD /* FBSimulatorPool+Private.h in Headers */,
|
||||
AACA2C371C2976B100979C45 /* FBAddVideoPolyfill.h in Headers */,
|
||||
AA9517A51C15F54600A89CAD /* FBTask.h in Headers */,
|
||||
AA4243011C529393008ABD80 /* FBCapacityQueue.h in Headers */,
|
||||
AAD4978A1C50F14B00ABC1A7 /* FBMutableSimulatorEventSink.h in Headers */,
|
||||
AA1D65461C21CD2A0069F90D /* FBASLParser.h in Headers */,
|
||||
AAF2D3561C33EA3100434516 /* FBSimulatorInteraction+Lifecycle.h in Headers */,
|
||||
|
@ -2066,7 +2133,9 @@
|
|||
AAB207C01C2099A9007C7908 /* FBSimulatorLoggingEventSink.h in Headers */,
|
||||
AA9517A21C15F54600A89CAD /* FBSimulatorSession.h in Headers */,
|
||||
AA2219911C3D868300371B01 /* FBProcessTerminationStrategy.h in Headers */,
|
||||
AA4242F91C529280008ABD80 /* FBFramebufferDebugWindow.h in Headers */,
|
||||
AA9517BC1C15F54600A89CAD /* NSRunLoop+SimulatorControlAdditions.h in Headers */,
|
||||
AA4242EA1C528338008ABD80 /* FBFramebufferDelegate.h in Headers */,
|
||||
AA95177E1C15F54600A89CAD /* FBSimulator+Private.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -2172,6 +2241,7 @@
|
|||
files = (
|
||||
AA78DCDB1C5005D2006FAB41 /* FBSimulatorLaunchCtl.m in Sources */,
|
||||
AA1D65431C21B38D0069F90D /* FBCrashLogInfo.m in Sources */,
|
||||
AA4242EE1C528338008ABD80 /* FBSimulatorFramebuffer.m in Sources */,
|
||||
AA9517521C15F54600A89CAD /* FBSimulatorConfiguration.m in Sources */,
|
||||
AA9517541C15F54600A89CAD /* FBSimulatorControlConfiguration.m in Sources */,
|
||||
AA9517881C15F54600A89CAD /* FBSimulatorPredicates.m in Sources */,
|
||||
|
@ -2183,10 +2253,13 @@
|
|||
AA9517C31C15F60B00A89CAD /* FBCompositeSimulatorEventSink.m in Sources */,
|
||||
AAD51EA41C3AEFEA00A763D0 /* FBSimulatorLaunchConfiguration+Helpers.m in Sources */,
|
||||
AA9517831C15F54600A89CAD /* FBSimulatorControl+PrincipalClass.m in Sources */,
|
||||
AA4242F21C529145008ABD80 /* FBFramebufferCompositeDelegate.m in Sources */,
|
||||
AA95179A1C15F54600A89CAD /* FBDispatchSourceNotifier.m in Sources */,
|
||||
AA9517681C15F54600A89CAD /* FBSimulatorInteraction+Applications.m in Sources */,
|
||||
AA9517781C15F54600A89CAD /* FBSimulatorLogs.m in Sources */,
|
||||
AABD8DFA1C592DBA008527CD /* FBFramebufferImage.m in Sources */,
|
||||
AA95177D1C15F54600A89CAD /* FBSimulator+Helpers.m in Sources */,
|
||||
AA4243021C529393008ABD80 /* FBCapacityQueue.m in Sources */,
|
||||
AA9517B91C15F54600A89CAD /* FBSimulatorError.m in Sources */,
|
||||
AAD51EA01C3ADECA00A763D0 /* FBSimulatorLaunchConfiguration.m in Sources */,
|
||||
AA9517BB1C15F54600A89CAD /* FBSimulatorLogger.m in Sources */,
|
||||
|
@ -2197,6 +2270,8 @@
|
|||
AA95179E1C15F54600A89CAD /* FBProcessQuery.m in Sources */,
|
||||
AA9517981C15F54600A89CAD /* FBCoreSimulatorNotifier.m in Sources */,
|
||||
AA9517731C15F54600A89CAD /* FBSimulatorInteraction+Video.m in Sources */,
|
||||
AA4242F61C529213008ABD80 /* FBFramebufferCounter.m in Sources */,
|
||||
AA4242FE1C529366008ABD80 /* FBFramebufferVideo.m in Sources */,
|
||||
AA9517BD1C15F54600A89CAD /* NSRunLoop+SimulatorControlAdditions.m in Sources */,
|
||||
AA2219961C3E752800371B01 /* FBCoreSimulatorTerminationStrategy.m in Sources */,
|
||||
AA9517AB1C15F54600A89CAD /* FBTaskExecutor.m in Sources */,
|
||||
|
@ -2232,6 +2307,7 @@
|
|||
AA9517561C15F54600A89CAD /* FBSimulatorControlGlobalConfiguration.m in Sources */,
|
||||
AA9517941C15F54600A89CAD /* FBSimulatorHistory.m in Sources */,
|
||||
AA9517611C15F54600A89CAD /* FBSimulatorNotificationEventSink.m in Sources */,
|
||||
AA4242FA1C529280008ABD80 /* FBFramebufferDebugWindow.m in Sources */,
|
||||
AAF8DA6E1C1AFFF0003B519E /* FBProcessQuery+Helpers.m in Sources */,
|
||||
AA0771F21C1ADFA300E7FD52 /* FBBinaryParser.m in Sources */,
|
||||
);
|
||||
|
@ -2317,6 +2393,8 @@
|
|||
DVTiPhoneSimulatorRemoteClient,
|
||||
"-weak_framework",
|
||||
CoreSimulator,
|
||||
"-weak_framework",
|
||||
SimulatorKit,
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -2361,6 +2439,8 @@
|
|||
DVTiPhoneSimulatorRemoteClient,
|
||||
"-weak_framework",
|
||||
CoreSimulator,
|
||||
"-weak_framework",
|
||||
SimulatorKit,
|
||||
);
|
||||
};
|
||||
name = Profile;
|
||||
|
@ -2405,6 +2485,8 @@
|
|||
DVTiPhoneSimulatorRemoteClient,
|
||||
"-weak_framework",
|
||||
CoreSimulator,
|
||||
"-weak_framework",
|
||||
SimulatorKit,
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
|
@ -2452,10 +2534,6 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.FBSimulatorControlTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
|
@ -2497,10 +2575,6 @@
|
|||
INFOPLIST_FILE = "FBSimulatorControlTests/FBSimulatorControlTests-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.FBSimulatorControlTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
|
@ -2542,10 +2616,6 @@
|
|||
INFOPLIST_FILE = "FBSimulatorControlTests/FBSimulatorControlTests-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.FBSimulatorControlTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
|
|
|
@ -12,6 +12,15 @@
|
|||
#import <FBSimulatorControl/FBDebugDescribeable.h>
|
||||
#import <FBSimulatorControl/FBJSONSerializationDescribeable.h>
|
||||
|
||||
/**
|
||||
An Option Set for Direct Launching.
|
||||
*/
|
||||
typedef NS_OPTIONS(NSUInteger, FBSimulatorLaunchOptions) {
|
||||
FBSimulatorLaunchOptionsEnableDirectLaunch = 1 << 0, /** Launches Simulators directly with a Framebuffer instead of with Simulator.app */
|
||||
FBSimulatorLaunchOptionsRecordVideo = 1 << 1, /** Records the Framebuffer to a video */
|
||||
FBSimulatorLaunchOptionsShowDebugWindow = 1 << 2, /** Relays the Simulator Framebuffer to a window */
|
||||
};
|
||||
|
||||
/**
|
||||
A Value Object for defining how to launch a Simulator.
|
||||
*/
|
||||
|
@ -27,6 +36,11 @@
|
|||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *scaleString;
|
||||
|
||||
/**
|
||||
Options for using a useFramebuffer App instead of Xcode's Simulator.app
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) FBSimulatorLaunchOptions options;
|
||||
|
||||
#pragma mark Default Instance
|
||||
|
||||
+ (instancetype)defaultConfiguration;
|
||||
|
@ -67,4 +81,12 @@
|
|||
+ (instancetype)withLocale:(NSLocale *)locale;
|
||||
- (instancetype)withLocale:(NSLocale *)locale;
|
||||
|
||||
#pragma mark Launch Options
|
||||
|
||||
/**
|
||||
Set Direct Launch Options
|
||||
*/
|
||||
+ (instancetype)withOptions:(FBSimulatorLaunchOptions)options;
|
||||
- (instancetype)withOptions:(FBSimulatorLaunchOptions)options;
|
||||
|
||||
@end
|
||||
|
|
|
@ -61,12 +61,12 @@
|
|||
static FBSimulatorLaunchConfiguration *configuration;
|
||||
dispatch_once(&onceToken, ^{
|
||||
id<FBSimulatorLaunchConfiguration_Scale> scale = FBSimulatorLaunchConfiguration_Scale_100.new;
|
||||
configuration = [[self alloc] initWithScale:scale locale:nil];
|
||||
configuration = [[self alloc] initWithScale:scale locale:nil options:0];
|
||||
});
|
||||
return configuration;
|
||||
}
|
||||
|
||||
- (instancetype)initWithScale:(id<FBSimulatorLaunchConfiguration_Scale>)scale locale:(NSLocale *)locale
|
||||
- (instancetype)initWithScale:(id<FBSimulatorLaunchConfiguration_Scale>)scale locale:(NSLocale *)locale options:(FBSimulatorLaunchOptions)options
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -75,6 +75,7 @@
|
|||
|
||||
_scale = scale;
|
||||
_locale = locale;
|
||||
_options = options;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -83,7 +84,7 @@
|
|||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
return [[self.class alloc] initWithScale:self.scale locale:self.locale];
|
||||
return [[self.class alloc] initWithScale:self.scale locale:self.locale options:self.options];
|
||||
}
|
||||
|
||||
#pragma mark NSCoding
|
||||
|
@ -97,6 +98,7 @@
|
|||
|
||||
_scale = [coder decodeObjectForKey:NSStringFromSelector(@selector(scale))];
|
||||
_locale = [coder decodeObjectForKey:NSStringFromSelector(@selector(locale))];
|
||||
_options = [[coder decodeObjectForKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -105,6 +107,7 @@
|
|||
{
|
||||
[coder encodeObject:self.scale forKey:NSStringFromSelector(@selector(scale))];
|
||||
[coder encodeObject:self.locale forKey:NSStringFromSelector(@selector(locale))];
|
||||
[coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))];
|
||||
}
|
||||
|
||||
#pragma mark NSObject
|
||||
|
@ -116,12 +119,13 @@
|
|||
}
|
||||
|
||||
return [self.scaleString isEqualToString:configuration.scaleString] &&
|
||||
(self.locale == configuration.locale || [self.locale isEqual:configuration.locale]);
|
||||
(self.locale == configuration.locale || [self.locale isEqual:configuration.locale]) &&
|
||||
self.options == configuration.options;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return self.scaleString.hash ^ self.locale.hash;
|
||||
return self.scaleString.hash ^ self.locale.hash ^ self.options;
|
||||
}
|
||||
|
||||
#pragma mark FBDebugDescribeable
|
||||
|
@ -129,9 +133,10 @@
|
|||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:
|
||||
@"Scale %@ | Locale %@",
|
||||
@"Scale %@ | Locale %@ | Options %lu",
|
||||
self.scaleString,
|
||||
self.locale
|
||||
self.locale,
|
||||
self.options
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -204,6 +209,14 @@
|
|||
return [self withScale:FBSimulatorLaunchConfiguration_Scale_100.new];
|
||||
}
|
||||
|
||||
- (instancetype)withScale:(id<FBSimulatorLaunchConfiguration_Scale>)scale
|
||||
{
|
||||
if (!scale) {
|
||||
return nil;
|
||||
}
|
||||
return [[self.class alloc] initWithScale:scale locale:self.locale options:self.options];
|
||||
}
|
||||
|
||||
#pragma mark Locale
|
||||
|
||||
+ (instancetype)withLocale:(NSLocale *)locale
|
||||
|
@ -213,7 +226,7 @@
|
|||
|
||||
- (instancetype)withLocale:(NSLocale *)locale
|
||||
{
|
||||
return [[self.class alloc] initWithScale:self.scale locale:locale];
|
||||
return [[self.class alloc] initWithScale:self.scale locale:locale options:self.options];
|
||||
}
|
||||
|
||||
+ (instancetype)withLocaleNamed:(NSString *)localeName
|
||||
|
@ -226,14 +239,16 @@
|
|||
return [self withLocale:[NSLocale localeWithLocaleIdentifier:localeIdentifier]];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
#pragma mark Framebuffer
|
||||
|
||||
- (instancetype)withScale:(id<FBSimulatorLaunchConfiguration_Scale>)scale
|
||||
+ (instancetype)withOptions:(FBSimulatorLaunchOptions)options
|
||||
{
|
||||
if (!scale) {
|
||||
return nil;
|
||||
}
|
||||
return [[self.class alloc] initWithScale:scale locale:self.locale];
|
||||
return [self.defaultConfiguration withOptions:options];
|
||||
}
|
||||
|
||||
- (instancetype)withOptions:(FBSimulatorLaunchOptions)options
|
||||
{
|
||||
return [[self.class alloc] initWithScale:self.scale locale:self.locale options:options];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -48,6 +48,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
for (id<FBSimulatorEventSink> sink in self.sinks) {
|
||||
[sink framebufferDidStart:framebuffer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
for (id<FBSimulatorEventSink> sink in self.sinks) {
|
||||
[sink framebufferDidTerminate:framebuffer expected:expected];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
for (id<FBSimulatorEventSink> sink in self.sinks) {
|
||||
|
|
|
@ -21,6 +21,16 @@
|
|||
[self.eventSink containerApplicationDidTerminate:applicationProcess expected:expected];
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
[self.eventSink framebufferDidStart:framebuffer];
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
[self.eventSink framebufferDidTerminate:framebuffer expected:expected];
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
[self.eventSink simulatorDidLaunch:launchdSimProcess];
|
||||
|
|
|
@ -39,4 +39,9 @@
|
|||
*/
|
||||
@property (nonatomic, copy, readonly) FBProcessInfo *containerApplication;
|
||||
|
||||
/**
|
||||
The current Framebuffer.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) FBSimulatorFramebuffer *framebuffer;
|
||||
|
||||
@end
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
#import "FBProcessQuery+Simulators.h"
|
||||
#import "FBProcessQuery.h"
|
||||
#import "FBSimulatorControlGlobalConfiguration.h"
|
||||
#import "FBSimulatorFramebuffer.h"
|
||||
|
||||
@interface FBSimulatorEventRelay ()
|
||||
|
||||
@property (nonatomic, copy, readwrite) FBProcessInfo *launchdSimProcess;
|
||||
@property (nonatomic, copy, readwrite) FBProcessInfo *containerApplication;
|
||||
@property (nonatomic, strong, readwrite) FBSimulatorFramebuffer *framebuffer;
|
||||
|
||||
@property (nonatomic, assign, readwrite) FBSimulatorState lastKnownState;
|
||||
@property (nonatomic, strong, readonly) NSMutableSet *knownLaunchedProcesses;
|
||||
|
@ -95,6 +97,24 @@
|
|||
[self.sink containerApplicationDidTerminate:applicationProcess expected:expected];
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
NSParameterAssert(framebuffer);
|
||||
NSParameterAssert(self.framebuffer == nil);
|
||||
|
||||
self.framebuffer = framebuffer;
|
||||
[self.sink framebufferDidStart:framebuffer];
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
NSParameterAssert(framebuffer);
|
||||
NSParameterAssert(self.framebuffer);
|
||||
|
||||
self.framebuffer = nil;
|
||||
[self.sink framebufferDidTerminate:framebuffer expected:expected];
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
NSParameterAssert(launchdSimProcess);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
@class FBProcessInfo;
|
||||
@class FBSimulator;
|
||||
@class FBSimulatorApplication;
|
||||
@class FBSimulatorFramebuffer;
|
||||
@class FBWritableLog;
|
||||
@protocol FBTerminationHandle;
|
||||
@protocol FBJSONSerializationDescribeable;
|
||||
|
@ -40,6 +41,21 @@
|
|||
*/
|
||||
- (void)containerApplicationDidTerminate:(FBProcessInfo *)applicationProcess expected:(BOOL)expected;
|
||||
|
||||
/**
|
||||
Event for the creation and validity of a Simulator Framebuffer.
|
||||
|
||||
@param framebuffer the Framebuffer of the Simulator.
|
||||
*/
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer;
|
||||
|
||||
/**
|
||||
Event for the termination of a Simulator Framebuffer.
|
||||
|
||||
@param framebuffer the Framebuffer of the Simulator.
|
||||
@param expected whether the termination was expected or not.
|
||||
*/
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected;
|
||||
|
||||
/**
|
||||
Event for the launch of a Simulator's launchd_sim.
|
||||
|
||||
|
|
|
@ -128,6 +128,15 @@
|
|||
{
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
}
|
||||
|
|
|
@ -56,6 +56,16 @@
|
|||
[self.logger logFormat:@"%@Container Application Did Terminate => %@ Expected %d", self.prefix, applicationProcess.shortDescription, expected];
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
[self.logger logFormat:@"%@Framebuffer Did Start => %@", self.prefix, framebuffer];
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
[self.logger logFormat:@"%@Framebuffer Did Terminate => %@ Expected %d", self.prefix, framebuffer, expected];
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
[self.logger logFormat:@"%@Simulator Did launch => %@", self.prefix, launchdSimProcess.shortDescription];
|
||||
|
|
|
@ -31,6 +31,16 @@ extern NSString *const FBSimulatorContainerDidLaunchNotification;
|
|||
*/
|
||||
extern NSString *const FBSimulatorContainerDidTerminateNotification;
|
||||
|
||||
/**
|
||||
Notification that is fired when a Simulator Framebuffer Starts.
|
||||
*/
|
||||
extern NSString *const FBSimulatorFramebufferDidStartNotification;
|
||||
|
||||
/**
|
||||
Notification that is fired when a Simulator Framebuffer Terminates.
|
||||
*/
|
||||
extern NSString *const FBSimulatorFramebufferDidTerminateNotification;
|
||||
|
||||
/**
|
||||
Notification that is fired when a Application Process Launches.
|
||||
*/
|
||||
|
@ -72,7 +82,12 @@ extern NSString *const FBSimulatorExpectedTerminationKey;
|
|||
extern NSString *const FBSimulatorProcessKey;
|
||||
|
||||
/**
|
||||
Notification UserInfo for a FBWritableLog Diagnostic Log.
|
||||
Notification UserInfo for the Framebuffer.
|
||||
*/
|
||||
extern NSString *const FBSimulatorFramebufferKey;
|
||||
|
||||
/**
|
||||
Notification UserInfo for the name of a diagnostic.
|
||||
*/
|
||||
extern NSString *const FBSimulatorDiagnosticLog;
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ NSString *const FBSimulatorDidLaunchNotification = @"FBSimulatorDidLaunchNotific
|
|||
NSString *const FBSimulatorDidTerminateNotification = @"FBSimulatorDidTerminateNotification";
|
||||
NSString *const FBSimulatorContainerDidLaunchNotification = @"FBSimulatorContainerDidLaunchNotification";
|
||||
NSString *const FBSimulatorContainerDidTerminateNotification = @"FBSimulatorContainerDidTerminateNotification";
|
||||
NSString *const FBSimulatorFramebufferDidStartNotification = @"FBSimulatorFramebufferDidStartNotification";
|
||||
NSString *const FBSimulatorFramebufferDidTerminateNotification = @"FBSimulatorFramebufferDidTerminateNotification";
|
||||
NSString *const FBSimulatorApplicationProcessDidLaunchNotification = @"FBSimulatorApplicationProcessDidLaunchNotification";
|
||||
NSString *const FBSimulatorApplicationProcessDidTerminateNotification = @"FBSimulatorApplicationProcessDidTerminateNotification";
|
||||
NSString *const FBSimulatorAgentProcessDidLaunchNotification = @"FBSimulatorAgentProcessDidLaunchNotification";
|
||||
|
@ -23,6 +25,7 @@ NSString *const FBSimulatorStateDidChange = @"FBSimulatorStateDidChange";
|
|||
NSString *const FBSimulatorExpectedTerminationKey = @"expected";
|
||||
NSString *const FBSimulatorProcessKey = @"process";
|
||||
NSString *const FBSimulatorDiagnosticLog = @"diagnostic_log";
|
||||
NSString *const FBSimulatorFramebufferKey = @"framebuffer";
|
||||
NSString *const FBSimulatorStateKey = @"simulator_state";
|
||||
|
||||
@interface FBSimulatorNotificationEventSink ()
|
||||
|
@ -57,6 +60,21 @@ NSString *const FBSimulatorStateKey = @"simulator_state";
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
[self materializeNotification:FBSimulatorFramebufferDidStartNotification userInfo:@{
|
||||
FBSimulatorFramebufferKey : framebuffer,
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
[self materializeNotification:FBSimulatorFramebufferDidTerminateNotification userInfo:@{
|
||||
FBSimulatorExpectedTerminationKey : @(expected),
|
||||
FBSimulatorFramebufferKey : framebuffer,
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
[self materializeNotification:FBSimulatorDidLaunchNotification userInfo:@{
|
||||
|
|
|
@ -72,6 +72,16 @@
|
|||
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import <FBSimulatorControl/FBASLParser.h>
|
||||
#import <FBSimulatorControl/FBAddVideoPolyfill.h>
|
||||
#import <FBSimulatorControl/FBBinaryParser.h>
|
||||
#import <FBSimulatorControl/FBCapacityQueue.h>
|
||||
#import <FBSimulatorControl/FBCollectionDescriptions.h>
|
||||
#import <FBSimulatorControl/FBCompositeSimulatorEventSink.h>
|
||||
#import <FBSimulatorControl/FBConcurrentCollectionOperations.h>
|
||||
|
@ -18,6 +19,11 @@
|
|||
#import <FBSimulatorControl/FBCrashLogInfo.h>
|
||||
#import <FBSimulatorControl/FBDebugDescribeable.h>
|
||||
#import <FBSimulatorControl/FBDispatchSourceNotifier.h>
|
||||
#import <FBSimulatorControl/FBFramebufferCompositeDelegate.h>
|
||||
#import <FBSimulatorControl/FBFramebufferCounter.h>
|
||||
#import <FBSimulatorControl/FBFramebufferDebugWindow.h>
|
||||
#import <FBSimulatorControl/FBFramebufferDelegate.h>
|
||||
#import <FBSimulatorControl/FBFramebufferVideo.h>
|
||||
#import <FBSimulatorControl/FBInteraction+Private.h>
|
||||
#import <FBSimulatorControl/FBInteraction.h>
|
||||
#import <FBSimulatorControl/FBJSONSerializationDescribeable.h>
|
||||
|
@ -46,6 +52,7 @@
|
|||
#import <FBSimulatorControl/FBSimulatorError.h>
|
||||
#import <FBSimulatorControl/FBSimulatorEventRelay.h>
|
||||
#import <FBSimulatorControl/FBSimulatorEventSink.h>
|
||||
#import <FBSimulatorControl/FBSimulatorFramebuffer.h>
|
||||
#import <FBSimulatorControl/FBSimulatorHistory+Private.h>
|
||||
#import <FBSimulatorControl/FBSimulatorHistory+Queries.h>
|
||||
#import <FBSimulatorControl/FBSimulatorHistory.h>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBSimulatorControl/FBFramebufferDelegate.h>
|
||||
|
||||
/**
|
||||
A Framebuffer Delegate that forwards all messages to an array of delegates.
|
||||
*/
|
||||
@interface FBFramebufferCompositeDelegate : NSObject <FBFramebufferDelegate>
|
||||
|
||||
/**
|
||||
A Composite Delegate that will notify an array of delegates.
|
||||
|
||||
@param delegates the delegates to call.
|
||||
@return a composite framebuffer delegate.
|
||||
*/
|
||||
+ (instancetype)withDelegates:(NSArray *)delegates;
|
||||
|
||||
@end
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBFramebufferCompositeDelegate.h"
|
||||
|
||||
@interface FBFramebufferCompositeDelegate ()
|
||||
|
||||
@property (nonatomic, copy, readwrite) NSArray *delegates;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFramebufferCompositeDelegate
|
||||
|
||||
+ (instancetype)withDelegates:(NSArray *)delegates
|
||||
{
|
||||
return [[FBFramebufferCompositeDelegate alloc] initWithDelegates:delegates];
|
||||
}
|
||||
|
||||
- (instancetype)initWithDelegates:(NSArray *)delegates
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_delegates = delegates;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark FBFramebufferDelegate Implementation
|
||||
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size
|
||||
{
|
||||
for (id<FBFramebufferDelegate> delegate in self.delegates) {
|
||||
[delegate framebuffer:framebuffer didGetSize:size];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size
|
||||
{
|
||||
for (id<FBFramebufferDelegate> delegate in self.delegates) {
|
||||
[delegate framebufferDidUpdate:framebuffer withImage:image size:size];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error
|
||||
{
|
||||
for (id<FBFramebufferDelegate> delegate in self.delegates) {
|
||||
[delegate framebufferDidBecomeInvalid:framebuffer error:error];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBSimulatorControl/FBFramebufferDelegate.h>
|
||||
|
||||
@protocol FBSimulatorLogger;
|
||||
|
||||
/**
|
||||
A Framebuffer Delegate that counts frames and prints at intervals
|
||||
*/
|
||||
@interface FBFramebufferCounter : NSObject <FBFramebufferDelegate>
|
||||
|
||||
/**
|
||||
Creates a new Framebuffer counter.
|
||||
|
||||
@param logFrequency the frequency with which to log frame counts
|
||||
@param logger the logger to log to
|
||||
*/
|
||||
+ (instancetype)withLogFrequency:(NSUInteger)logFrequency logger:(id<FBSimulatorLogger>)logger;
|
||||
|
||||
/**
|
||||
The Frame Count.
|
||||
*/
|
||||
@property (atomic, assign, readonly) NSUInteger frameCount;
|
||||
|
||||
@end
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBFramebufferCounter.h"
|
||||
|
||||
#import "FBSimulatorLogger.h"
|
||||
|
||||
@interface FBFramebufferCounter ()
|
||||
|
||||
@property (nonatomic, strong, readonly) id<FBSimulatorLogger> logger;
|
||||
@property (nonatomic, assign, readonly) NSUInteger logFrequency;
|
||||
|
||||
@property (atomic, assign, readwrite) NSUInteger frameCount;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFramebufferCounter
|
||||
|
||||
+ (instancetype)withLogFrequency:(NSUInteger)logFrequency logger:(id<FBSimulatorLogger>)logger
|
||||
{
|
||||
return [[self alloc] initWithLogFrequency:logFrequency logger:logger];
|
||||
}
|
||||
|
||||
- (instancetype)initWithLogFrequency:(NSUInteger)logFrequency logger:(id<FBSimulatorLogger>)logger
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_logFrequency = logFrequency;
|
||||
_logger = logger;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSObject
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%lu", (unsigned long)self.frameCount];
|
||||
}
|
||||
|
||||
#pragma mark FBFramebufferCounterDelegate Implementation
|
||||
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size
|
||||
{
|
||||
self.frameCount++;
|
||||
if (self.frameCount % self.logFrequency != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.logger.info logFormat:@"Frame Count %lu", self.frameCount];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBSimulatorControl/FBFramebufferDelegate.h>
|
||||
|
||||
/**
|
||||
A Framebuffer Delegate that renders to a window.
|
||||
|
||||
This will create an NSApplication in the current process so buyer beware.
|
||||
It is intended to be used for debugging purposes only.
|
||||
*/
|
||||
@interface FBFramebufferDebugWindow : NSObject <FBFramebufferDelegate>
|
||||
|
||||
/**
|
||||
Creates and returns an object that will display the framebuffer in a window.
|
||||
|
||||
@param name the name of the Window.
|
||||
@return a new FBFramebufferDebugWindow instance.
|
||||
*/
|
||||
+ (instancetype)withName:(NSString *)name;
|
||||
|
||||
@end
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBFramebufferDebugWindow.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface FBFramebufferDebugWindow () <NSApplicationDelegate>
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *name;
|
||||
@property (nonatomic, strong, readwrite) NSWindow *window;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFramebufferDebugWindow
|
||||
|
||||
@synthesize window = _window;
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)withName:(NSString *)name;
|
||||
{
|
||||
return [[self alloc] initWithName:name];
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
{
|
||||
NSParameterAssert(name);
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_name = name;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self teardownWindow];
|
||||
}
|
||||
|
||||
#pragma mark FBFramebufferDelegate Implementation
|
||||
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.window = [self createWindowWithSize:size];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateWindowWithImage:image];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error
|
||||
{
|
||||
[self teardownWindow];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (void)teardownWindow
|
||||
{
|
||||
NSWindow *window = self.window;
|
||||
self.window = nil;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[window close];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateWindowWithImage:(CGImageRef)image
|
||||
{
|
||||
self.window.contentView.layer.contents = (__bridge id) image;
|
||||
}
|
||||
|
||||
- (NSWindow *)createWindowWithSize:(CGSize)size
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
[NSApp setDelegate:self];
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
NSRect initialPosition = [[NSScreen mainScreen] frame];
|
||||
initialPosition = (NSRect) { .size = size, .origin = CGPointZero };
|
||||
NSWindow *window = [[NSWindow alloc] initWithContentRect:initialPosition styleMask:NSTitledWindowMask | NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO];
|
||||
window.backgroundColor = NSColor.whiteColor;
|
||||
window.contentView.wantsLayer = YES;
|
||||
window.title = self.name;
|
||||
[window makeKeyAndOrderFront:NSApp];
|
||||
[window display];
|
||||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
#pragma mark NSApplicationDelegate
|
||||
|
||||
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
||||
{
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
|
||||
{
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FBSimulatorFramebuffer;
|
||||
|
||||
/**
|
||||
A Delegate for Framebuffer related activity.
|
||||
*/
|
||||
@protocol FBFramebufferDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Called when the Size of the Framebuffer becomes available.
|
||||
Will be called before frames are sent.
|
||||
|
||||
@param framebuffer the framebuffer that was updated.
|
||||
@param size the size of the framebuffer.
|
||||
*/
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size;
|
||||
|
||||
/**
|
||||
Called when a new image frame is available.
|
||||
|
||||
@param framebuffer the framebuffer that was updated.
|
||||
@param size the size of the image.
|
||||
@param image the updated image.
|
||||
*/
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size;
|
||||
|
||||
/**
|
||||
Called when the framebuffer is no longer valid, typically when the Simulator shuts down.
|
||||
|
||||
@param framebuffer the framebuffer that was updated.
|
||||
*/
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error;
|
||||
|
||||
@end
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBSimulatorControl/FBFramebufferDelegate.h>
|
||||
|
||||
@class FBWritableLog;
|
||||
@protocol FBSimulatorEventSink;
|
||||
|
||||
/**
|
||||
A Simulator Framebuffer Delegate that stores an image of the most recent image.
|
||||
|
||||
When a Framebuffer is teared down, all it's delegates will be too.
|
||||
Just as this occurs, this class will report the image to the Event Sink.
|
||||
This means that the final frame will be captured.
|
||||
*/
|
||||
@interface FBFramebufferImage : NSObject <FBFramebufferDelegate>
|
||||
|
||||
/**
|
||||
Creates a new FBFramebufferImage instance.
|
||||
|
||||
@param writableLog the Writable Log to base image reporting off.
|
||||
@param eventSink the Event Sink to report Image Logs to.
|
||||
@return a new FBFramebufferImage instance.
|
||||
*/
|
||||
+ (instancetype)withWritableLog:(FBWritableLog *)writableLog eventSink:(id<FBSimulatorEventSink>)eventSink;
|
||||
|
||||
/**
|
||||
Writes a PNG to file and updates the Writable Log.
|
||||
|
||||
@param image the image to update the log with.
|
||||
@param writableLog the log to base the new log off.
|
||||
@return a new FBWritableLog with a path to the image on succcess, the original log on failure.
|
||||
*/
|
||||
+ (FBWritableLog *)appendImage:(CGImageRef)image toWritableLog:(FBWritableLog *)writableLog;
|
||||
|
||||
/**
|
||||
The Latest Image from the Framebuffer.
|
||||
*/
|
||||
@property (atomic, assign, readonly) CGImageRef image;
|
||||
|
||||
@end
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBFramebufferImage.h"
|
||||
|
||||
#import "FBWritableLog.h"
|
||||
#import "FBSimulatorEventSink.h"
|
||||
|
||||
@interface FBFramebufferImage ()
|
||||
|
||||
@property (atomic, assign, readwrite) CGImageRef image;
|
||||
|
||||
@property (nonatomic, strong, readonly) FBWritableLog *writableLog;
|
||||
@property (nonatomic, strong, readonly) id<FBSimulatorEventSink> eventSink;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFramebufferImage
|
||||
|
||||
+ (instancetype)withWritableLog:(FBWritableLog *)writableLog eventSink:(id<FBSimulatorEventSink>)eventSink
|
||||
{
|
||||
return [[self alloc] initWithWritableLog:writableLog eventSink:eventSink];
|
||||
}
|
||||
|
||||
- (instancetype)initWithWritableLog:(FBWritableLog *)writableLog eventSink:(id<FBSimulatorEventSink>)eventSink
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_writableLog = [writableLog copy];
|
||||
_eventSink = eventSink;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGImageRelease(_image);
|
||||
}
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
+ (FBWritableLog *)appendImage:(CGImageRef)image toWritableLog:(FBWritableLog *)writableLog
|
||||
{
|
||||
FBWritableLogBuilder *builder = [FBWritableLogBuilder builderWithWritableLog:writableLog];
|
||||
NSString *filePath = [builder createPath];
|
||||
NSURL *url = [NSURL fileURLWithPath:filePath];
|
||||
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(
|
||||
(__bridge CFURLRef) url,
|
||||
kUTTypePNG,
|
||||
1,
|
||||
NULL
|
||||
);
|
||||
if (!url) {
|
||||
return writableLog;
|
||||
}
|
||||
CGImageDestinationAddImage(destination, image, NULL);
|
||||
if (!CGImageDestinationFinalize(destination)) {
|
||||
return writableLog;
|
||||
}
|
||||
CFRelease(destination);
|
||||
|
||||
return [[builder updatePath:filePath] build];
|
||||
}
|
||||
|
||||
#pragma mark FBFramebufferCounterDelegate Implementation
|
||||
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size
|
||||
{
|
||||
CGImageRef oldImage = self.image;
|
||||
self.image = CGImageRetain(image);
|
||||
CGImageRelease(oldImage);
|
||||
}
|
||||
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error
|
||||
{
|
||||
FBWritableLog *log = [FBFramebufferImage appendImage:self.image toWritableLog:self.writableLog];
|
||||
id<FBSimulatorEventSink> eventSink = self.eventSink;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[eventSink logAvailable:log];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBSimulatorControl/FBFramebufferDelegate.h>
|
||||
|
||||
@class FBWritableLog;
|
||||
@protocol FBSimulatorLogger;
|
||||
@protocol FBSimulatorEventSink;
|
||||
|
||||
/**
|
||||
A Simulator Framebuffer Delegate that encodes video and writes to a file.
|
||||
|
||||
All media activity is serialized on a queue, this queue is internal and should not be used by clients.
|
||||
The video will be created as soon as the first frame is available.
|
||||
*/
|
||||
@interface FBFramebufferVideo : NSObject <FBFramebufferDelegate>
|
||||
|
||||
/**
|
||||
Creates a new FBFramebufferVideo instance.
|
||||
|
||||
@param writableLog the log to base the video file from.
|
||||
@param scale the scaling factor of the video. Must be 1 or lower.
|
||||
@param logger the logger object to log events to, may be nil.
|
||||
@param eventSink an event sink to report video output to.
|
||||
@return a new FBFramebufferVideo instance.
|
||||
*/
|
||||
+ (instancetype)withWritableLog:(FBWritableLog *)writableLog scale:(CGFloat)scale logger:(id<FBSimulatorLogger>)logger eventSink:(id<FBSimulatorEventSink>)eventSink;
|
||||
|
||||
/**
|
||||
Stops the recording recording of the video framebuffer.
|
||||
|
||||
@param error an error out for any error that occurs.
|
||||
@return YES if successful, NO otherwise.
|
||||
*/
|
||||
- (BOOL)stopRecordingWithError:(NSError **)error;
|
||||
|
||||
@end
|
|
@ -0,0 +1,397 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBFramebufferVideo.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreVideo/CVPixelBuffer.h>
|
||||
#import <CoreVideo/CoreVideo.h>
|
||||
|
||||
#import "FBCapacityQueue.h"
|
||||
#import "FBSimulatorError.h"
|
||||
#import "FBSimulatorEventSink.h"
|
||||
#import "FBSimulatorLogger.h"
|
||||
#import "FBWritableLog.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, FBFramebufferVideoState) {
|
||||
FBFramebufferVideoStateNotStarted = 0,
|
||||
FBFramebufferVideoStateRunning = 1,
|
||||
FBFramebufferVideoStateTerminated = 2,
|
||||
};
|
||||
|
||||
static const OSType FBFramebufferPixelFormat = kCVPixelFormatType_32ARGB;
|
||||
static const CMTimeScale FBFramebufferTimescale = 1000000000;
|
||||
|
||||
@interface FBFramebufferVideoItem : NSObject
|
||||
|
||||
@property (nonatomic, assign, readonly) CMTime time;
|
||||
@property (nonatomic, assign, readonly) CGImageRef image;
|
||||
|
||||
- (instancetype)initWithTime:(CMTime)time image:(CGImageRef)image;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFramebufferVideoItem
|
||||
|
||||
- (instancetype)initWithTime:(CMTime)time image:(CGImageRef)image
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_time = time;
|
||||
_image = CGImageRetain(image);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGImageRelease(_image);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FBFramebufferVideo ()
|
||||
|
||||
@property (nonatomic, strong, readonly) FBWritableLog *writableLog;
|
||||
@property (nonatomic, assign, readonly) CGFloat scale;
|
||||
@property (nonatomic, strong, readonly) id<FBSimulatorLogger> logger;
|
||||
@property (nonatomic, strong, readonly) id<FBSimulatorEventSink> eventSink;
|
||||
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t mediaQueue;
|
||||
@property (nonatomic, strong, readonly) FBCapacityQueue *itemQueue;
|
||||
|
||||
@property (nonatomic, assign, readwrite) CMTimebaseRef timebase;
|
||||
@property (nonatomic, assign, readwrite) CGSize size;
|
||||
|
||||
@property (nonatomic, strong, readwrite) AVAssetWriter *writer;
|
||||
@property (nonatomic, strong, readwrite) AVAssetWriterInput *input;
|
||||
@property (nonatomic, strong, readwrite) AVAssetWriterInputPixelBufferAdaptor *adaptor;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFramebufferVideo
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)withWritableLog:(FBWritableLog *)writableLog scale:(CGFloat)scale logger:(id<FBSimulatorLogger>)logger eventSink:(id<FBSimulatorEventSink>)eventSink
|
||||
{
|
||||
return [[self alloc] initWithWritableLog:writableLog scale:scale logger:logger eventSink:eventSink];
|
||||
}
|
||||
|
||||
- (instancetype)initWithWritableLog:(FBWritableLog *)writableLog scale:(CGFloat)scale logger:(id<FBSimulatorLogger>)logger eventSink:(id<FBSimulatorEventSink>)eventSink
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_writableLog = writableLog;
|
||||
_scale = scale;
|
||||
_logger = logger;
|
||||
_eventSink = eventSink;
|
||||
_mediaQueue = dispatch_queue_create("com.facebook.FBSimulatorControl.media", DISPATCH_QUEUE_SERIAL);
|
||||
_itemQueue = [FBCapacityQueue withCapacity:20];
|
||||
|
||||
_size = CGSizeZero;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Public Methods
|
||||
|
||||
- (BOOL)stopRecordingWithError:(NSError **)error
|
||||
{
|
||||
__block BOOL success = NO;
|
||||
// A barrier is used to ensure the contract of finishWritingWithCompletionHandler: is fulfilled:
|
||||
// "To guarantee that all sample buffers are successfully written, you must ensure that all calls to appendSampleBuffer: and appendPixelBuffer:withPresentationTime: have returned"
|
||||
dispatch_barrier_sync(self.mediaQueue, ^{
|
||||
if (!self.writer) {
|
||||
success = [[FBSimulatorError describe:@"Cannot stop recording when it hasn't started"] failBool:error];
|
||||
return;
|
||||
}
|
||||
[self teardownWriter];
|
||||
success = YES;
|
||||
});
|
||||
return success;
|
||||
}
|
||||
|
||||
#pragma mark FBFramebufferDelegate Implementation
|
||||
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size
|
||||
{
|
||||
dispatch_async(self.mediaQueue, ^{
|
||||
NSParameterAssert(CGSizeEqualToSize(self.size, CGSizeZero));
|
||||
self.size = CGSizeMake(ceil(size.width * self.scale), ceil(size.height * self.scale));
|
||||
[self startRecordingWithError:nil];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size
|
||||
{
|
||||
dispatch_async(self.mediaQueue, ^{
|
||||
// Don't append frames if the writer hasn't been constructed yet.s
|
||||
if (!self.writer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an item and place it in the queue.
|
||||
CMTime time = CMTimebaseGetTimeWithTimeScale(self.timebase, FBFramebufferTimescale, kCMTimeRoundingMethod_Default);
|
||||
FBFramebufferVideoItem *item = [[FBFramebufferVideoItem alloc] initWithTime:time image:image];
|
||||
FBFramebufferVideoItem *evictedItem = [self.itemQueue push:item];
|
||||
if (evictedItem) {
|
||||
[self.logger.debug logFormat:@"Evicted frame at time %f, frame dropped", CMTimeGetSeconds(item.time)];
|
||||
}
|
||||
|
||||
[self drainQueue];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error
|
||||
{
|
||||
dispatch_barrier_async(self.mediaQueue, ^{
|
||||
[self teardownWriter];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
NSParameterAssert([keyPath isEqualToString:@"readyForMoreMediaData"]);
|
||||
if (![change[NSKeyValueChangeNewKey] boolValue]) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(self.mediaQueue, ^{
|
||||
[self drainQueue];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSDictionary *)pixelBufferAttributes
|
||||
{
|
||||
CGSize size = self.size;
|
||||
return @{
|
||||
(NSString *) kCVPixelBufferCGImageCompatibilityKey:(id)kCFBooleanTrue,
|
||||
(NSString *) kCVPixelBufferCGBitmapContextCompatibilityKey:(id)kCFBooleanTrue,
|
||||
(NSString *) kCVPixelBufferWidthKey : @(size.width),
|
||||
(NSString *) kCVPixelBufferHeightKey : @(size.height),
|
||||
(NSString *) kCVPixelBufferPixelFormatTypeKey : @(FBFramebufferPixelFormat)
|
||||
};
|
||||
}
|
||||
|
||||
- (void)drainQueue
|
||||
{
|
||||
while (self.input.readyForMoreMediaData) {
|
||||
FBFramebufferVideoItem *item = [self.itemQueue pop];
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
CVPixelBufferRef pixelBuffer = [FBFramebufferVideo createPixelBufferOfSize:self.size attributes:self.pixelBufferAttributes ofImage:item.image];
|
||||
if (!pixelBuffer) {
|
||||
return;
|
||||
}
|
||||
if (![self.adaptor appendPixelBuffer:pixelBuffer withPresentationTime:item.time]) {
|
||||
[self.logger.error logFormat:@"Failed to append frame at time %f seconds of pixel buffer with error %@", CMTimeGetSeconds(item.time), self.writer.error];
|
||||
}
|
||||
CVPixelBufferRelease(pixelBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)startRecordingWithError:(NSError **)error
|
||||
{
|
||||
// Confirm that framebuffer size info is available
|
||||
if (CGSizeEqualToSize(CGSizeZero, self.size)) {
|
||||
return [[[FBSimulatorError describe:@"Video size not yet available, cannot record"] logger:self.logger] failBool:error];
|
||||
}
|
||||
|
||||
// Create a timebase that has now as the start.
|
||||
CMTimebaseRef timebase = NULL;
|
||||
CMTimebaseCreateWithMasterClock(
|
||||
kCFAllocatorDefault,
|
||||
CMClockGetHostTimeClock(),
|
||||
&timebase
|
||||
);
|
||||
NSAssert(timebase, @"Expected to be able to construct timebase");
|
||||
CMTimebaseSetRate(timebase, 1.0);
|
||||
self.timebase = timebase;
|
||||
|
||||
// Create the asset writer.
|
||||
FBWritableLogBuilder *logBuilder = [FBWritableLogBuilder builderWithWritableLog:self.writableLog];
|
||||
NSString *path = logBuilder.createPath;
|
||||
if (![self createAssetWriterAtPath:path size:self.size error:error]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Report the availability of the video
|
||||
[self.eventSink logAvailable:[[logBuilder updatePath:path] build]];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)createAssetWriterAtPath:(NSString *)videoPath size:(CGSize)size error:(NSError **)error
|
||||
{
|
||||
// Create an Asset Writer to a file
|
||||
NSError *innerError = nil;
|
||||
NSURL *url = [NSURL fileURLWithPath:videoPath];
|
||||
AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:url fileType:AVFileTypeMPEG4 error:&innerError];
|
||||
if (!writer) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to create an asset writer at %@", videoPath]
|
||||
causedBy:innerError]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Create an Input for the Writer
|
||||
NSDictionary *outputSettings = @{
|
||||
AVVideoCodecKey : AVVideoCodecH264,
|
||||
AVVideoWidthKey : @(size.width),
|
||||
AVVideoHeightKey : @(size.height),
|
||||
};
|
||||
AVAssetWriterInput *input = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
|
||||
input.expectsMediaDataInRealTime = NO;
|
||||
if (![writer canAddInput:input]) {
|
||||
return [[FBSimulatorError
|
||||
describeFormat:@"Not permitted to add writer input at %@", input]
|
||||
failBool:error];
|
||||
}
|
||||
// Setting a Fragment interval will ensure there is a video if the process crashes.
|
||||
// However, setting this appears to make the output fail after a period of time.
|
||||
// This can be set with:
|
||||
// writer.movieFragmentInterval = CMTimeMakeWithSeconds(5, FBFramebufferTimescale);
|
||||
[writer addInput:input];
|
||||
|
||||
// Create an adaptor for writing to the input via concrete pixel buffers
|
||||
AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
|
||||
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:input
|
||||
sourcePixelBufferAttributes:nil];
|
||||
|
||||
// If the file exists at the path it must be removed first.
|
||||
NSFileManager *fileManager = NSFileManager.defaultManager;
|
||||
if ([fileManager fileExistsAtPath:videoPath] && ![fileManager removeItemAtPath:videoPath error:&innerError]) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to remove item at path %@ prior to deletion", videoPath]
|
||||
causedBy:innerError]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Start the Writer and the Session
|
||||
if (![writer startWriting]) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to start writing to the writer %@ error code %ld", writer, writer.status]
|
||||
causedBy:writer.error]
|
||||
failBool:error];
|
||||
}
|
||||
[writer startSessionAtSourceTime:kCMTimeZero];
|
||||
|
||||
// Success means the state needs to be set.
|
||||
self.writer = writer;
|
||||
self.input = input;
|
||||
self.adaptor = adaptor;
|
||||
[writer addObserver:self forKeyPath:@"readyForMoreMediaData" options:NSKeyValueObservingOptionNew context:NULL];
|
||||
|
||||
// Log the success
|
||||
[self.logger.info logFormat:@"Started Recording video at path %@", videoPath];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)teardownWriter
|
||||
{
|
||||
AVAssetWriter *writer = self.writer;
|
||||
AVAssetWriterInput *input = self.input;
|
||||
self.writer = nil;
|
||||
self.adaptor = nil;
|
||||
self.input = nil;
|
||||
|
||||
[input markAsFinished];
|
||||
[writer removeObserver:self forKeyPath:@"readyForMoreMediaData"];
|
||||
[writer finishWritingWithCompletionHandler:^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.logger.info log:@"Finished Recording"];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
+ (CVPixelBufferRef)createPixelBufferOfSize:(CGSize)size attributes:(NSDictionary *)attributes ofImage:(CGImageRef)image
|
||||
{
|
||||
size_t width = (size_t) size.width;
|
||||
size_t height = (size_t) size.height;
|
||||
OSType pixelFormat = FBFramebufferPixelFormat;
|
||||
|
||||
// Create the Pixel Buffer, caller will release.
|
||||
CVPixelBufferRef pixelBuffer = NULL;
|
||||
CVReturn status = CVPixelBufferCreate(
|
||||
kCFAllocatorDefault,
|
||||
width,
|
||||
height,
|
||||
pixelFormat,
|
||||
(__bridge CFDictionaryRef) attributes,
|
||||
&pixelBuffer
|
||||
);
|
||||
if (status != kCVReturnSuccess) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return [self writeImage:image ofSize:size intoPixelBuffer:pixelBuffer];
|
||||
}
|
||||
|
||||
+ (CVPixelBufferRef)createPixelBufferOfSize:(CGSize)size fromPool:(CVPixelBufferPoolRef)pool ofImage:(CGImageRef)image
|
||||
{
|
||||
// Get the pixel buffer from the pool
|
||||
CVPixelBufferRef pixelBuffer = NULL;
|
||||
CVReturn status = CVPixelBufferPoolCreatePixelBuffer(
|
||||
NULL,
|
||||
pool,
|
||||
&pixelBuffer
|
||||
);
|
||||
if (status != kCVReturnSuccess) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return [self writeImage:image ofSize:size intoPixelBuffer:pixelBuffer];
|
||||
}
|
||||
|
||||
+ (CVPixelBufferRef)writeImage:(CGImageRef)image ofSize:(CGSize)size intoPixelBuffer:(CVPixelBufferRef)pixelBuffer
|
||||
{
|
||||
// Get and lock the buffer.
|
||||
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
||||
void *buffer = CVPixelBufferGetBaseAddress(pixelBuffer);
|
||||
|
||||
// Create a graphics context based on the pixel buffer.
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
buffer,
|
||||
(size_t) size.width,
|
||||
(size_t) size.height,
|
||||
8, // See CGBitmapContextCreate documentation
|
||||
CVPixelBufferGetBytesPerRow(pixelBuffer),
|
||||
colorSpace,
|
||||
(CGBitmapInfo) kCGImageAlphaNoneSkipFirst
|
||||
);
|
||||
|
||||
// Draw to it.
|
||||
CGRect rect = { .size = size, .origin = CGPointZero };
|
||||
CGContextDrawImage(
|
||||
context,
|
||||
rect,
|
||||
image
|
||||
);
|
||||
|
||||
// Cleanup.
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextRelease(context);
|
||||
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
||||
|
||||
return pixelBuffer;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBSimulatorControl/FBJSONSerializationDescribeable.h>
|
||||
|
||||
@class FBSimulator;
|
||||
@class FBSimulatorLaunchConfiguration;
|
||||
@class SimDeviceFramebufferService;
|
||||
@protocol FBFramebufferDelegate;
|
||||
|
||||
/**
|
||||
A container and client for a Simulator's Framebuffer that forwards important events to delegates.
|
||||
|
||||
The class itself doesn't perform much behaviour other than to manage the lifecycle.
|
||||
Implementors of FBFramebufferDelegate perform individual behaviours such as recording videos and images.
|
||||
*/
|
||||
@interface FBSimulatorFramebuffer : NSObject <FBJSONSerializationDescribeable>
|
||||
|
||||
/**
|
||||
Creates and returns a new FBSimulatorDirectLaunch object for the provided SimDeviceFramebufferService.
|
||||
|
||||
@param framebufferService the SimDeviceFramebufferService to connect to.
|
||||
@param launchConfiguration the launch configuration to create the service for.
|
||||
@param simulator the Simulator to which the Framebuffer belongs.
|
||||
@return a new FBSimulatorDirectLaunch instance. Must not be nil.
|
||||
*/
|
||||
+ (instancetype)withFramebufferService:(SimDeviceFramebufferService *)framebufferService configuration:(FBSimulatorLaunchConfiguration *)launchConfiguration simulator:(FBSimulator *)simulator;
|
||||
|
||||
/**
|
||||
Starts listening for Framebuffer events on a background queue.
|
||||
Events are delivered to the Event Sink on this same background queue.
|
||||
*/
|
||||
- (void)startListeningInBackground;
|
||||
|
||||
/**
|
||||
Stops listening for Framebuffer envents on the background queue.
|
||||
Events are delivered to the Event Sink on this same background queue.
|
||||
*/
|
||||
- (void)stopListening;
|
||||
|
||||
@end
|
|
@ -0,0 +1,210 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBSimulatorFramebuffer.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import <SimulatorKit/SimDeviceFramebufferBackingStore.h>
|
||||
#import <SimulatorKit/SimDeviceFramebufferService.h>
|
||||
|
||||
#import "FBFramebufferCompositeDelegate.h"
|
||||
#import "FBFramebufferCounter.h"
|
||||
#import "FBFramebufferDebugWindow.h"
|
||||
#import "FBFramebufferDelegate.h"
|
||||
#import "FBFramebufferImage.h"
|
||||
#import "FBFramebufferVideo.h"
|
||||
#import "FBSimulator.h"
|
||||
#import "FBSimulatorEventSink.h"
|
||||
#import "FBSimulatorLaunchConfiguration.h"
|
||||
#import "FBSimulatorLogger.h"
|
||||
#import "FBSimulatorLogs.h"
|
||||
#import "FBWritableLog.h"
|
||||
|
||||
/**
|
||||
Enumeration to keep track of internal state.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, FBSimulatorFramebufferState) {
|
||||
FBSimulatorFramebufferStateNotStarted = 0, /** Before the framebuffer is 'listening'. */
|
||||
FBSimulatorFramebufferStateStarting = 1, /** After the framebuffer has started, but before the first frame. */
|
||||
FBSimulatorFramebufferStateRunning = 2, /** After the framebuffer has started, but before the first frame. */
|
||||
FBSimulatorFramebufferStateTerminated = 3, /** After the framebuffer has terminated. */
|
||||
};
|
||||
|
||||
static const NSInteger FBFramebufferLogFrameFrequency = 100;
|
||||
|
||||
@interface FBSimulatorFramebuffer () <FBFramebufferDelegate>
|
||||
|
||||
@property (nonatomic, strong, readonly) SimDeviceFramebufferService *framebufferService;
|
||||
@property (nonatomic, strong, readonly) FBFramebufferCounter *counter;
|
||||
@property (nonatomic, strong, readonly) id<FBSimulatorEventSink> eventSink;
|
||||
|
||||
@property (nonatomic, strong, readonly) id<FBFramebufferDelegate> delegate;
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
|
||||
|
||||
@property (nonatomic, assign, readwrite) FBSimulatorFramebufferState state;
|
||||
@property (nonatomic, assign, readwrite) CGSize size;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBSimulatorFramebuffer
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)withFramebufferService:(SimDeviceFramebufferService *)framebufferService configuration:(FBSimulatorLaunchConfiguration *)launchConfiguration simulator:(FBSimulator *)simulator {
|
||||
NSMutableArray *sinks = [NSMutableArray array];
|
||||
BOOL useWindow = (launchConfiguration.options & FBSimulatorLaunchOptionsShowDebugWindow) == FBSimulatorLaunchOptionsShowDebugWindow;
|
||||
if (useWindow) {
|
||||
[sinks addObject:[FBFramebufferDebugWindow withName:@"Simulator"]];
|
||||
}
|
||||
|
||||
BOOL recordVideo = (launchConfiguration.options & FBSimulatorLaunchOptionsRecordVideo) == FBSimulatorLaunchOptionsRecordVideo;
|
||||
if (recordVideo) {
|
||||
NSDecimalNumber *scaleNumber = [NSDecimalNumber decimalNumberWithString:launchConfiguration.scaleString];
|
||||
[sinks addObject:[FBFramebufferVideo withWritableLog:simulator.logs.video scale:scaleNumber.floatValue logger:simulator.logger eventSink:simulator.eventSink]];
|
||||
}
|
||||
|
||||
FBFramebufferCounter *counter = [FBFramebufferCounter withLogFrequency:FBFramebufferLogFrameFrequency logger:simulator.logger];
|
||||
[sinks addObject:counter];
|
||||
[sinks addObject:[FBFramebufferImage withWritableLog:simulator.logs.screenshot eventSink:simulator.eventSink]];
|
||||
|
||||
id<FBFramebufferDelegate> delegate = [FBFramebufferCompositeDelegate withDelegates:[sinks copy]];
|
||||
return [[self alloc] initWithFramebufferService:framebufferService counter:counter eventSink:simulator.eventSink delegate:delegate];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFramebufferService:(SimDeviceFramebufferService *)framebufferService counter:(FBFramebufferCounter *)counter eventSink:(id<FBSimulatorEventSink>)eventSink delegate:(id<FBFramebufferDelegate>)delegate
|
||||
{
|
||||
NSParameterAssert(framebufferService);
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_framebufferService = framebufferService;
|
||||
_counter = counter;
|
||||
_eventSink = eventSink;
|
||||
_delegate = delegate;
|
||||
|
||||
_queue = dispatch_queue_create("com.facebook.FBSimulatorControl.simulatorframebuffer", DISPATCH_QUEUE_SERIAL);
|
||||
_state = FBSimulatorFramebufferStateNotStarted;
|
||||
_size = CGSizeZero;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSObject
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:
|
||||
@"%@ | Size %@ | Frame Counter %@",
|
||||
[FBSimulatorFramebuffer stringFromFramebufferState:self.state],
|
||||
NSStringFromSize(self.size),
|
||||
self.counter
|
||||
];
|
||||
}
|
||||
|
||||
#pragma mark FBJSONSerializationDescribeable Implementation
|
||||
|
||||
- (id)jsonSerializableRepresentation
|
||||
{
|
||||
return @{
|
||||
@"size" : NSStringFromSize(self.size)
|
||||
};
|
||||
}
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
- (void)startListeningInBackground;
|
||||
{
|
||||
NSParameterAssert(self.state == FBSimulatorFramebufferStateNotStarted);
|
||||
|
||||
self.state = FBSimulatorFramebufferStateStarting;
|
||||
[self.framebufferService registerClient:self onQueue:self.queue];
|
||||
[self.framebufferService resume];
|
||||
}
|
||||
|
||||
- (void)stopListening
|
||||
{
|
||||
NSParameterAssert(self.state != FBSimulatorFramebufferStateNotStarted);
|
||||
NSParameterAssert(self.state != FBSimulatorFramebufferStateTerminated);
|
||||
|
||||
[self framebufferDidBecomeInvalid:self error:nil];
|
||||
}
|
||||
|
||||
#pragma mark Client Callbacks from SimDeviceFramebufferService
|
||||
|
||||
- (void)framebufferService:(SimDeviceFramebufferService *)service didFailWithError:(NSError *)error
|
||||
{
|
||||
[self.delegate framebufferDidBecomeInvalid:self error:error];
|
||||
}
|
||||
|
||||
- (void)framebufferService:(SimDeviceFramebufferService *)service didRotateToAngle:(double)angle
|
||||
{
|
||||
}
|
||||
|
||||
- (void)framebufferService:(SimDeviceFramebufferService *)service didUpdateRegion:(CGRect)region ofBackingStore:(SimDeviceFramebufferBackingStore *)backingStore
|
||||
{
|
||||
[self framebuffer:self didGetSize:CGSizeMake(backingStore.pixelsWide, backingStore.pixelsHigh)];
|
||||
[self.delegate framebufferDidUpdate:self withImage:backingStore.image size:NSMakeSize(backingStore.pixelsWide, backingStore.pixelsHigh)];
|
||||
}
|
||||
|
||||
#pragma mark Internal Delegate Forwarding
|
||||
|
||||
- (void)framebuffer:(FBSimulatorFramebuffer *)framebuffer didGetSize:(CGSize)size
|
||||
{
|
||||
if (self.state != FBSimulatorFramebufferStateStarting) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.state = FBSimulatorFramebufferStateRunning;
|
||||
[self.delegate framebuffer:framebuffer didGetSize:size];
|
||||
}
|
||||
|
||||
- (void)framebufferDidUpdate:(FBSimulatorFramebuffer *)framebuffer withImage:(CGImageRef)image size:(CGSize)size
|
||||
{
|
||||
if (self.state != FBSimulatorFramebufferStateRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.delegate framebufferDidUpdate:framebuffer withImage:image size:size];
|
||||
}
|
||||
|
||||
- (void)framebufferDidBecomeInvalid:(FBSimulatorFramebuffer *)framebuffer error:(NSError *)error
|
||||
{
|
||||
if (self.state != FBSimulatorFramebufferStateStarting && self.state != FBSimulatorFramebufferStateRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.framebufferService unregisterClient:self];
|
||||
[self.framebufferService suspend];
|
||||
[self.delegate framebufferDidBecomeInvalid:self error:error];
|
||||
[self.eventSink framebufferDidTerminate:self expected:(error != nil)];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
+ (NSString *)stringFromFramebufferState:(FBSimulatorFramebufferState)state
|
||||
{
|
||||
switch (state) {
|
||||
case FBSimulatorFramebufferStateNotStarted:
|
||||
return @"Not Started";
|
||||
case FBSimulatorFramebufferStateStarting:
|
||||
return @"Starting";
|
||||
case FBSimulatorFramebufferStateRunning:
|
||||
return @"Running";
|
||||
case FBSimulatorFramebufferStateTerminated:
|
||||
return @"Terminated";
|
||||
default:
|
||||
return @"Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#import <CoreSimulator/SimDevice.h>
|
||||
|
||||
#import <SimulatorKit/SimDeviceFramebufferService.h>
|
||||
|
||||
#import "FBCollectionDescriptions.h"
|
||||
#import "FBInteraction+Private.h"
|
||||
#import "FBProcessInfo.h"
|
||||
|
@ -27,6 +29,7 @@
|
|||
#import "FBSimulatorControlGlobalConfiguration.h"
|
||||
#import "FBSimulatorError.h"
|
||||
#import "FBSimulatorEventSink.h"
|
||||
#import "FBSimulatorFramebuffer.h"
|
||||
#import "FBSimulatorInteraction+Private.h"
|
||||
#import "FBSimulatorLaunchConfiguration+Helpers.h"
|
||||
#import "FBSimulatorLaunchConfiguration.h"
|
||||
|
@ -45,95 +48,18 @@
|
|||
|
||||
- (instancetype)bootSimulator:(FBSimulatorLaunchConfiguration *)configuration
|
||||
{
|
||||
NSParameterAssert(configuration);
|
||||
|
||||
return [self interactWithShutdownSimulator:^ BOOL (NSError **error, FBSimulator *simulator) {
|
||||
// Fetch the Boot arguments
|
||||
NSError *innerError = nil;
|
||||
NSArray *arguments = [configuration xcodeSimulatorApplicationArgumentsForSimulator:simulator error:&innerError];
|
||||
if (!arguments) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to create boot args for Configuration %@", configuration]
|
||||
causedBy:innerError]
|
||||
failBool:error];
|
||||
BOOL useDirectLaunch = (configuration.options & FBSimulatorLaunchOptionsEnableDirectLaunch) == FBSimulatorLaunchOptionsEnableDirectLaunch;
|
||||
if (useDirectLaunch) {
|
||||
return [FBSimulatorInteraction launchSimulatorDirectly:simulator configuration:configuration error:error];
|
||||
}
|
||||
|
||||
// Construct and start the task.
|
||||
id<FBTask> task = [[[[[FBTaskExecutor.sharedInstance
|
||||
withLaunchPath:FBSimulatorApplication.xcodeSimulator.binary.path]
|
||||
withArguments:[arguments copy]]
|
||||
withEnvironmentAdditions:@{ FBSimulatorControlSimulatorLaunchEnvironmentSimulatorUDID : simulator.udid }]
|
||||
build]
|
||||
startAsynchronously];
|
||||
|
||||
[simulator.eventSink terminationHandleAvailable:task];
|
||||
|
||||
// Expect no immediate error.
|
||||
if (task.error) {
|
||||
return [[[[FBSimulatorError
|
||||
describe:@"Failed to Launch Simulator Process"]
|
||||
causedBy:task.error]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Expect the state of the simulator to be updated.
|
||||
BOOL didBoot = [simulator waitOnState:FBSimulatorStateBooted];
|
||||
if (!didBoot) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Timed out waiting for device to be Booted, got %@", simulator.device.stateString]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
|
||||
// Expect the launch info for the process to exist.
|
||||
FBProcessQuery *processQuery = simulator.processQuery;
|
||||
FBProcessInfo *containerApplication = [simulator.processQuery simulatorApplicationProcessForSimDevice:simulator.device];
|
||||
if (!containerApplication) {
|
||||
return [[[FBSimulatorError
|
||||
describe:@"Could not obtain process info for container application"]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
[simulator.eventSink containerApplicationDidLaunch:containerApplication];
|
||||
|
||||
// Expect the launchd_sim process to be updated.
|
||||
FBProcessInfo *launchdSimProcess = [processQuery launchdSimProcessForSimDevice:simulator.device];
|
||||
if (!launchdSimProcess) {
|
||||
return [[[FBSimulatorError
|
||||
describe:@"Could not obtain process info for launchd_sim process"]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
[simulator.eventSink simulatorDidLaunch:launchdSimProcess];
|
||||
|
||||
// Waitng for all required processes to start
|
||||
NSSet *requiredProcessNames = simulator.requiredProcessNamesToVerifyBooted;
|
||||
BOOL didStartAllRequiredProcesses = [NSRunLoop.mainRunLoop spinRunLoopWithTimeout:FBSimulatorControlGlobalConfiguration.slowTimeout untilTrue:^ BOOL {
|
||||
NSSet *runningProcessNames = [NSSet setWithArray:[[processQuery subprocessesOf:launchdSimProcess.processIdentifier] valueForKey:@"processName"]];
|
||||
return [requiredProcessNames isSubsetOfSet:runningProcessNames];
|
||||
}];
|
||||
if (!didStartAllRequiredProcesses) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Timed out waiting for all required processes %@ to start", [FBCollectionDescriptions oneLineDescriptionFromArray:requiredProcessNames.allObjects]]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Pass on the success to the event sink.
|
||||
[simulator.eventSink containerApplicationDidLaunch:containerApplication];
|
||||
|
||||
return YES;
|
||||
return [FBSimulatorInteraction launchSimulatorFromXcodeApplication:simulator configuration:configuration error:error];
|
||||
}];
|
||||
}
|
||||
|
||||
- (instancetype)shutdownSimulator
|
||||
{
|
||||
return [self interactWithBootedSimulator:^ BOOL (NSError **error, FBSimulator *simulator) {
|
||||
FBProcessInfo *containerApplication = simulator.containerApplication;
|
||||
FBProcessInfo *launchdSimProcess = simulator.launchdSimProcess;
|
||||
|
||||
FBSimulatorTerminationStrategy *terminationStrategy = [FBSimulatorTerminationStrategy
|
||||
withConfiguration:simulator.pool.configuration
|
||||
processQuery:simulator.processQuery
|
||||
|
@ -143,8 +69,6 @@
|
|||
if (![terminationStrategy killSimulators:@[simulator] withError:&innerError]) {
|
||||
return [[[[FBSimulatorError describe:@"Could not shutdown simulator"] inSimulator:simulator] causedBy:innerError] failBool:error];
|
||||
}
|
||||
[simulator.eventSink containerApplicationDidTerminate:containerApplication expected:YES];
|
||||
[simulator.eventSink simulatorDidTerminate:launchdSimProcess expected:YES];
|
||||
|
||||
return YES;
|
||||
}];
|
||||
|
@ -218,4 +142,137 @@
|
|||
return [self signal:SIGKILL process:process];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
+ (BOOL)launchSimulatorDirectly:(FBSimulator *)simulator configuration:(FBSimulatorLaunchConfiguration *)configuration error:(NSError **)error
|
||||
{
|
||||
// Creating the Framebuffer with the 'mainScreen' constructor will return a 'PurpleFBServer' and attach it to the '_registeredServices' ivar.
|
||||
// This is the Framebuffer for the Simulator's main screen, which is distinct from 'PurpleFBTVOut' and 'Stark' Framebuffers for External Displays and CarPlay.
|
||||
NSError *innerError = nil;
|
||||
SimDeviceFramebufferService *framebufferService = [NSClassFromString(@"SimDeviceFramebufferService") mainScreenFramebufferServiceForDevice:simulator.device error:&innerError];
|
||||
if (!framebufferService) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to create the Main Screen Framebuffer for device %@", simulator.device]
|
||||
causedBy:innerError]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// The 'register-head-services' option will attach the existing 'frameBufferService' when the Simulator is booted.
|
||||
// Simulator.app behaves similarly, except we can't peek at the Framebuffer as it is in a protected process since Xcode 7.
|
||||
// Prior to Xcode 6 it was possible to shim into the Simulator process but codesigning now prevents this https://gist.github.com/lawrencelomax/27bdc4e8a433a601008f
|
||||
NSDictionary *options = @{
|
||||
@"register-head-services" : @YES
|
||||
};
|
||||
|
||||
// Booting is simpler than the Simulator.app launch process since the caller calls CoreSimulator Framework directly.
|
||||
// Just pass in the options to ensure that the framebuffer service is registered when the Simulator is booted.
|
||||
BOOL success = [simulator.device bootWithOptions:options error:&innerError];
|
||||
if (!success) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to boot Simulator with options %@", options]
|
||||
causedBy:innerError]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Create and start the consumer of the Framebuffer Service.
|
||||
// The launch configuration will define the way that the Framebuffer is consumed.
|
||||
// Then the simulator's event sink should be notified with the created framebuffer object.
|
||||
FBSimulatorFramebuffer *framebuffer = [FBSimulatorFramebuffer withFramebufferService:framebufferService configuration:configuration simulator:simulator];
|
||||
[framebuffer startListeningInBackground];
|
||||
[simulator.eventSink framebufferDidStart:framebuffer];
|
||||
|
||||
// Expect the launchd_sim process to be updated.
|
||||
if (![self launchdSimWithAllRequiredProcessesForSimulator:simulator error:&innerError]) {
|
||||
return [FBSimulatorError failBoolWithError:innerError errorOut:error];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)launchSimulatorFromXcodeApplication:(FBSimulator *)simulator configuration:(FBSimulatorLaunchConfiguration *)configuration error:(NSError **)error
|
||||
{
|
||||
// Fetch the Boot arguments
|
||||
NSError *innerError = nil;
|
||||
NSArray *arguments = [configuration xcodeSimulatorApplicationArgumentsForSimulator:simulator error:&innerError];
|
||||
if (!arguments) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Failed to create boot args for Configuration %@", configuration]
|
||||
causedBy:innerError]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Construct and start the task.
|
||||
id<FBTask> task = [[[[[FBTaskExecutor.sharedInstance
|
||||
withLaunchPath:FBSimulatorApplication.xcodeSimulator.binary.path]
|
||||
withArguments:[arguments copy]]
|
||||
withEnvironmentAdditions:@{ FBSimulatorControlSimulatorLaunchEnvironmentSimulatorUDID : simulator.udid }]
|
||||
build]
|
||||
startAsynchronously];
|
||||
|
||||
[simulator.eventSink terminationHandleAvailable:task];
|
||||
|
||||
// Expect no immediate error.
|
||||
if (task.error) {
|
||||
return [[[[FBSimulatorError
|
||||
describe:@"Failed to Launch Simulator Process"]
|
||||
causedBy:task.error]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Expect the state of the simulator to be updated.
|
||||
BOOL didBoot = [simulator waitOnState:FBSimulatorStateBooted];
|
||||
if (!didBoot) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Timed out waiting for device to be Booted, got %@", simulator.device.stateString]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Expect the launch info for the process to exist.
|
||||
FBProcessInfo *containerApplication = [simulator.processQuery simulatorApplicationProcessForSimDevice:simulator.device];
|
||||
if (!containerApplication) {
|
||||
return [[[FBSimulatorError
|
||||
describe:@"Could not obtain process info for container application"]
|
||||
inSimulator:simulator]
|
||||
failBool:error];
|
||||
}
|
||||
[simulator.eventSink containerApplicationDidLaunch:containerApplication];
|
||||
|
||||
// Expect the launchd_sim process to be updated.
|
||||
if (![self launchdSimWithAllRequiredProcessesForSimulator:simulator error:error]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (FBProcessInfo *)launchdSimWithAllRequiredProcessesForSimulator:(FBSimulator *)simulator error:(NSError **)error
|
||||
{
|
||||
FBProcessQuery *processQuery = simulator.processQuery;
|
||||
FBProcessInfo *launchdSimProcess = [processQuery launchdSimProcessForSimDevice:simulator.device];
|
||||
if (!launchdSimProcess) {
|
||||
return [[[FBSimulatorError
|
||||
describe:@"Could not obtain process info for launchd_sim process"]
|
||||
inSimulator:simulator]
|
||||
fail:error];
|
||||
}
|
||||
[simulator.eventSink simulatorDidLaunch:launchdSimProcess];
|
||||
|
||||
// Waitng for all required processes to start
|
||||
NSSet *requiredProcessNames = simulator.requiredProcessNamesToVerifyBooted;
|
||||
BOOL didStartAllRequiredProcesses = [NSRunLoop.mainRunLoop spinRunLoopWithTimeout:FBSimulatorControlGlobalConfiguration.slowTimeout untilTrue:^ BOOL {
|
||||
NSSet *runningProcessNames = [NSSet setWithArray:[[processQuery subprocessesOf:launchdSimProcess.processIdentifier] valueForKey:@"processName"]];
|
||||
return [requiredProcessNames isSubsetOfSet:runningProcessNames];
|
||||
}];
|
||||
if (!didStartAllRequiredProcesses) {
|
||||
return [[[FBSimulatorError
|
||||
describeFormat:@"Timed out waiting for all required processes %@ to start", [FBCollectionDescriptions oneLineDescriptionFromArray:requiredProcessNames.allObjects]]
|
||||
inSimulator:simulator]
|
||||
fail:error];
|
||||
}
|
||||
|
||||
return launchdSimProcess;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -36,6 +36,11 @@ extern NSString *const FBSimulatorLogNameSimulatorBootstrap;
|
|||
*/
|
||||
extern NSString *const FBSimulatorLogNameVideo;
|
||||
|
||||
/**
|
||||
The Name of the Screenshot Log.
|
||||
*/
|
||||
extern NSString *const FBSimulatorLogNameScreenshot;
|
||||
|
||||
/**
|
||||
Exposes Simulator Logs & Diagnsotics as FBWritableLog instances.
|
||||
|
||||
|
@ -78,6 +83,11 @@ extern NSString *const FBSimulatorLogNameVideo;
|
|||
*/
|
||||
- (FBWritableLog *)video;
|
||||
|
||||
/**
|
||||
A Screenshot of the Simulator.
|
||||
*/
|
||||
- (FBWritableLog *)screenshot;
|
||||
|
||||
/**
|
||||
Crash logs of all the subprocesses that have crashed in the Simulator after the specified date.
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ NSString *const FBSimulatorLogNameSyslog = @"system_log";
|
|||
NSString *const FBSimulatorLogNameCoreSimulator = @"coresimulator";
|
||||
NSString *const FBSimulatorLogNameSimulatorBootstrap = @"launchd_bootstrap";
|
||||
NSString *const FBSimulatorLogNameVideo = @"video";
|
||||
NSString *const FBSimulatorLogNameScreenshot = @"screenshot";
|
||||
|
||||
@interface FBSimulatorLogs ()
|
||||
|
||||
|
@ -106,6 +107,15 @@ NSString *const FBSimulatorLogNameVideo = @"video";
|
|||
build];
|
||||
}
|
||||
|
||||
- (FBWritableLog *)screenshot
|
||||
{
|
||||
return [[[[self.logBuilder
|
||||
updateShortName:FBSimulatorLogNameScreenshot]
|
||||
updateFileType:@"png"]
|
||||
updateWritableLog:self.eventLogs[FBSimulatorLogNameVideo]]
|
||||
build];
|
||||
}
|
||||
|
||||
- (NSArray *)subprocessCrashesAfterDate:(NSDate *)date
|
||||
{
|
||||
return [FBConcurrentCollectionOperations
|
||||
|
@ -187,6 +197,16 @@ NSString *const FBSimulatorLogNameVideo = @"video";
|
|||
|
||||
}
|
||||
|
||||
- (void)framebufferDidStart:(FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)framebufferDidTerminate:(FBSimulatorFramebuffer *)framebuffer expected:(BOOL)expected
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)simulatorDidLaunch:(FBProcessInfo *)launchdSimProcess
|
||||
{
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
@class FBProcessInfo;
|
||||
@class FBProcessQuery;
|
||||
@class FBSimulatorConfiguration;
|
||||
@class FBSimulatorFramebuffer;
|
||||
@class FBSimulatorHistory;
|
||||
@class FBSimulatorLogger;
|
||||
@class FBSimulatorLogs;
|
||||
|
@ -141,6 +142,11 @@ typedef NS_ENUM(NSInteger, FBSimulatorProductFamily) {
|
|||
*/
|
||||
@property (nonatomic, copy, readonly) FBProcessInfo *containerApplication;
|
||||
|
||||
/**
|
||||
The Framebuffer of the Simulator.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) FBSimulatorFramebuffer *framebuffer;
|
||||
|
||||
/**
|
||||
The FBSimulatorLogs instance for fetching logs for the Simulator.
|
||||
*/
|
||||
|
|
|
@ -145,6 +145,11 @@
|
|||
return self.eventRelay.launchdSimProcess;
|
||||
}
|
||||
|
||||
- (FBSimulatorFramebuffer *)framebuffer
|
||||
{
|
||||
return self.eventRelay.framebuffer;
|
||||
}
|
||||
|
||||
- (FBProcessInfo *)containerApplication
|
||||
{
|
||||
return self.eventRelay.containerApplication;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#import "FBSimulatorControl+PrincipalClass.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import <CoreSimulator/NSUserDefaults-SimDefaults.h>
|
||||
#import <CoreSimulator/SimDevice.h>
|
||||
#import <CoreSimulator/SimRuntime.h>
|
||||
|
@ -79,6 +81,7 @@
|
|||
// 5) Provide a sanity check that any preloaded Private Frameworks match the current xcode-select version
|
||||
NSDictionary *classMapping = @{
|
||||
@"SimDevice" : @"Library/PrivateFrameworks/CoreSimulator.framework",
|
||||
@"SimDeviceFramebufferService" : @"Library/PrivateFrameworks/SimulatorKit.framework",
|
||||
@"DVTDevice" : @"../SharedFrameworks/DVTFoundation.framework",
|
||||
@"DTiPhoneSimulatorApplicationSpecifier" : @"../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework"
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#import "FBSimulatorControl.h"
|
||||
#import "FBSimulatorControlConfiguration.h"
|
||||
#import "FBSimulatorError.h"
|
||||
#import "FBSimulatorFramebuffer.h"
|
||||
#import "FBSimulatorInteraction.h"
|
||||
#import "FBSimulatorLogger.h"
|
||||
#import "FBSimulatorPredicates.h"
|
||||
|
@ -99,11 +100,13 @@
|
|||
|
||||
[self.logger.debug logFormat:@"Killing %@", [FBCollectionDescriptions oneLineDescriptionFromArray:simulators atKeyPath:@"shortDescription"]];
|
||||
for (FBSimulator *simulator in simulators) {
|
||||
FBProcessInfo *simulatorProcess = simulator.containerApplication ?: [self.processQuery simulatorApplicationProcessForSimDevice:simulator.device];
|
||||
// Get some preconditions
|
||||
NSError *innerError = nil;
|
||||
FBProcessInfo *launchdSimProcess = simulator.launchdSimProcess ?: [self.processQuery launchdSimProcessForSimDevice:simulator.device];
|
||||
|
||||
// Kill the Simulator.app Process first, see documentation in `-[FBSimDeviceWrapper shutdownWithError:]`.
|
||||
// This prevents 'Zombie' Simulator.app from existing.
|
||||
FBProcessInfo *simulatorProcess = simulator.containerApplication ?: [self.processQuery simulatorApplicationProcessForSimDevice:simulator.device];
|
||||
if (simulatorProcess) {
|
||||
[self.logger.debug logFormat:@"Simulator %@ has a Simulator.app Process %@, terminating it now", simulator.shortDescription, simulatorProcess];
|
||||
if (![self.processTerminationStrategy killProcess:simulatorProcess error:&innerError]) {
|
||||
|
@ -114,10 +117,21 @@
|
|||
logger:self.logger]
|
||||
fail:error];
|
||||
}
|
||||
[simulator.eventSink containerApplicationDidTerminate:simulatorProcess expected:YES];
|
||||
} else {
|
||||
[self.logger.debug logFormat:@"Simulator %@ does not have a running Simulator.app Process", simulator.shortDescription];
|
||||
}
|
||||
|
||||
// The Framebuffer should also be tidied up if one exists.
|
||||
FBSimulatorFramebuffer *framebuffer = simulator.framebuffer;
|
||||
if (framebuffer) {
|
||||
[self.logger.debug logFormat:@"Simulator %@ has a framebuffer %@, stopping now", simulator.shortDescription, framebuffer];
|
||||
// Stopping listening will notify the event sink.
|
||||
[framebuffer stopListening];
|
||||
} else {
|
||||
[self.logger.debug logFormat:@"Simulator %@ does not have a running Framebuffer", simulator.shortDescription];
|
||||
}
|
||||
|
||||
// Shutdown will:
|
||||
// 1) Wait for a Simulator launched via Simulator.app to be in a consistent 'Shutdown' state.
|
||||
// 2) Shutdown a SimDevice that has been launched directly via. `-[SimDevice bootWithOptions:error]`.
|
||||
|
@ -129,6 +143,9 @@
|
|||
logger:self.logger]
|
||||
fail:error];
|
||||
}
|
||||
if (launchdSimProcess) {
|
||||
[simulator.eventSink simulatorDidTerminate:launchdSimProcess expected:YES];
|
||||
}
|
||||
}
|
||||
return simulators;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ static void EnsureCGIsInitialized(void)
|
|||
return [pids containsObject:processIdentifier];
|
||||
}];
|
||||
// Each Simulator Process appears to have multiple Windows, with strange bounds.
|
||||
// We just care about the named one, which is the Simulator.app window with the Simulator's canvas.
|
||||
// We just care about the named one, which is the Simulator.app window with the Simulator's framebuffer.
|
||||
NSPredicate *namePredicate = [NSPredicate predicateWithBlock:^ BOOL (NSDictionary *window, NSDictionary *_) {
|
||||
NSString *windowName = window[(NSString *)kCGWindowName];
|
||||
return windowName.length > 0;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
A Simple Queue that Pushes to the end and pops from the front.
|
||||
Evicts from the front when capacity is reached.
|
||||
*/
|
||||
@interface FBCapacityQueue : NSObject
|
||||
|
||||
/**
|
||||
Constructs a queue with a capacity. Zero means no capacity limit.
|
||||
|
||||
@param capacity the capacity of the queue.
|
||||
@return a new Capacity Queue.
|
||||
*/
|
||||
+ (instancetype)withCapacity:(NSUInteger)capacity;
|
||||
|
||||
/**
|
||||
Pushes an item to the end of the queue.
|
||||
|
||||
@param item the item to push.
|
||||
@return the item that was evicted if capacity was reached.
|
||||
*/
|
||||
- (id)push:(id)item;
|
||||
|
||||
/**
|
||||
Pops an item from the front of the front of the queue.
|
||||
|
||||
@return the item at the front of the queue, nil otherwise.
|
||||
*/
|
||||
- (id)pop;
|
||||
|
||||
/**
|
||||
The count of the queue.
|
||||
|
||||
@return the count of items in the queue
|
||||
*/
|
||||
- (NSUInteger)count;
|
||||
|
||||
@end
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBCapacityQueue.h"
|
||||
|
||||
@interface FBCapacityQueue ()
|
||||
|
||||
@property (nonatomic, strong, readonly) NSMutableArray *array;
|
||||
@property (nonatomic, assign, readonly) NSUInteger capacity;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBCapacityQueue
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)withCapacity:(NSUInteger)capacity
|
||||
{
|
||||
return [[self alloc] initWithCapacity:capacity];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCapacity:(NSUInteger)capacity
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_array = [NSMutableArray array];
|
||||
_capacity = capacity;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
- (id)push:(id)item
|
||||
{
|
||||
if (self.array.count < self.capacity) {
|
||||
[self.array addObject:item];
|
||||
return nil;
|
||||
}
|
||||
|
||||
id evicted = [self.array firstObject];
|
||||
[self.array removeObjectAtIndex:0];
|
||||
[self.array addObject:item];
|
||||
return evicted;
|
||||
}
|
||||
|
||||
- (id)pop
|
||||
{
|
||||
if (self.array.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
id item = [self.array firstObject];
|
||||
[self.array removeObjectAtIndex:0];
|
||||
return item;
|
||||
}
|
||||
|
||||
- (NSUInteger)count
|
||||
{
|
||||
return self.array.count;
|
||||
}
|
||||
|
||||
@end
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#import <FBSimulatorControl/FBSimulatorControl.h>
|
||||
|
||||
#import "FBSimulatorControlTestCase.h"
|
||||
|
||||
@implementation XCTestCase (FBSimulatorControlAssertions)
|
||||
|
||||
#pragma mark Interactions
|
||||
|
@ -63,7 +65,13 @@
|
|||
{
|
||||
XCTAssertEqual(simulator.state, FBSimulatorStateBooted);
|
||||
XCTAssertNotNil(simulator.launchdSimProcess);
|
||||
XCTAssertNotNil(simulator.containerApplication);
|
||||
if (self.expectContainerProcesses) {
|
||||
XCTAssertNotNil(simulator.containerApplication);
|
||||
XCTAssertNil(simulator.framebuffer);
|
||||
} else {
|
||||
XCTAssertNil(simulator.containerApplication);
|
||||
XCTAssertNotNil(simulator.framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)assertSimulatorShutdown:(FBSimulator *)simulator
|
||||
|
@ -85,6 +93,13 @@
|
|||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (BOOL)expectContainerProcesses
|
||||
{
|
||||
return !FBSimulatorControlTestCase.useDirectLaunching;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FBSimulatorControlNotificationAssertions ()
|
||||
|
@ -122,6 +137,8 @@
|
|||
NSArray *notificationNames = @[
|
||||
FBSimulatorDidLaunchNotification,
|
||||
FBSimulatorDidTerminateNotification,
|
||||
FBSimulatorFramebufferDidStartNotification,
|
||||
FBSimulatorFramebufferDidTerminateNotification,
|
||||
FBSimulatorContainerDidLaunchNotification,
|
||||
FBSimulatorContainerDidTerminateNotification,
|
||||
FBSimulatorApplicationProcessDidLaunchNotification,
|
||||
|
@ -263,11 +280,17 @@
|
|||
|
||||
- (NSArray *)expectedBootNotificationNames
|
||||
{
|
||||
if (FBSimulatorControlTestCase.useDirectLaunching) {
|
||||
return @[FBSimulatorDidLaunchNotification, FBSimulatorFramebufferDidStartNotification];
|
||||
}
|
||||
return @[FBSimulatorDidLaunchNotification, FBSimulatorContainerDidLaunchNotification];
|
||||
}
|
||||
|
||||
- (NSArray *)expectedShutdownNotificationNames
|
||||
{
|
||||
if (FBSimulatorControlTestCase.useDirectLaunching) {
|
||||
return @[FBSimulatorDidTerminateNotification, FBSimulatorFramebufferDidTerminateNotification];
|
||||
}
|
||||
return @[FBSimulatorDidTerminateNotification, FBSimulatorContainerDidTerminateNotification];
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
@class FBSimulatorLaunchConfiguration;
|
||||
@class FBSimulatorSession;
|
||||
|
||||
/**
|
||||
Environment Keys and Values for how the Simulator should be launched.
|
||||
*/
|
||||
extern NSString *const FBSimulatorControlTestsLaunchTypeEnvKey;
|
||||
extern NSString *const FBSimulatorControlTestsLaunchTypeSimulatorApp;
|
||||
extern NSString *const FBSimulatorControlTestsLaunchTypeDirect;
|
||||
|
||||
/**
|
||||
A Test Case that boostraps a FBSimulatorControl instance.
|
||||
Should be overridden to provide Integration tests for Simulators.
|
||||
|
@ -85,4 +92,9 @@
|
|||
*/
|
||||
+ (BOOL)isRunningOnTravis;
|
||||
|
||||
/**
|
||||
Whether or not Simulators should be launched directly or via the Simulator.app.
|
||||
*/
|
||||
+ (BOOL)useDirectLaunching;
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,6 +17,12 @@ static NSString *const DeviceSetEnvKey = @"FBSIMULATORCONTROL_DEVICE_SET";
|
|||
static NSString *const DeviceSetEnvDefault = @"default";
|
||||
static NSString *const DeviceSetEnvCustom = @"custom";
|
||||
|
||||
static NSString *const LaunchTypeEnvKey = @"FBSIMULATORCONTROL_LAUNCH_TYPE";
|
||||
static NSString *const LaunchTypeSimulatorApp = @"simulator_app";
|
||||
static NSString *const LaunchTypeDirect = @"direct";
|
||||
|
||||
static NSString *const DirectLaunchEnableVideoRecording = @"FBSIMULATORCONTROL_RECORD_VIDEO";
|
||||
|
||||
@interface FBSimulatorControlTestCase ()
|
||||
|
||||
@end
|
||||
|
@ -99,6 +105,14 @@ static NSString *const DeviceSetEnvCustom = @"custom";
|
|||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)useDirectLaunching
|
||||
{
|
||||
if ([NSProcessInfo.processInfo.environment[LaunchTypeEnvKey] isEqualToString:LaunchTypeSimulatorApp]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSString *)defaultDeviceSetPath
|
||||
{
|
||||
NSString *value = NSProcessInfo.processInfo.environment[DeviceSetEnvKey];
|
||||
|
@ -108,6 +122,18 @@ static NSString *const DeviceSetEnvCustom = @"custom";
|
|||
return nil;
|
||||
}
|
||||
|
||||
+ (FBSimulatorLaunchConfiguration *)defaultLaunchConfiguration
|
||||
{
|
||||
if (self.useDirectLaunching) {
|
||||
FBSimulatorLaunchOptions options = FBSimulatorLaunchOptionsEnableDirectLaunch;
|
||||
if (NSProcessInfo.processInfo.environment[DirectLaunchEnableVideoRecording]) {
|
||||
options = (options | FBSimulatorLaunchOptionsRecordVideo);
|
||||
}
|
||||
return [FBSimulatorLaunchConfiguration withOptions:options];
|
||||
}
|
||||
return FBSimulatorLaunchConfiguration.defaultConfiguration;
|
||||
}
|
||||
|
||||
#pragma mark XCTestCase
|
||||
|
||||
- (void)setUp
|
||||
|
@ -115,7 +141,7 @@ static NSString *const DeviceSetEnvCustom = @"custom";
|
|||
self.managementOptions = FBSimulatorManagementOptionsKillSpuriousSimulatorsOnFirstStart | FBSimulatorManagementOptionsIgnoreSpuriousKillFail;
|
||||
self.allocationOptions = FBSimulatorAllocationOptionsReuse | FBSimulatorAllocationOptionsCreate | FBSimulatorAllocationOptionsEraseOnAllocate;
|
||||
self.simulatorConfiguration = FBSimulatorConfiguration.iPhone5;
|
||||
self.simulatorLaunchConfiguration = FBSimulatorLaunchConfiguration.defaultConfiguration;
|
||||
self.simulatorLaunchConfiguration = FBSimulatorControlTestCase.defaultLaunchConfiguration;
|
||||
self.deviceSetPath = FBSimulatorControlTestCase.defaultDeviceSetPath;
|
||||
}
|
||||
|
||||
|
@ -125,4 +151,5 @@ static NSString *const DeviceSetEnvCustom = @"custom";
|
|||
_control = nil;
|
||||
_assert = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -10,21 +10,6 @@ typedef void (^CDUnknownBlockType)(void); // return type and parameters are unkn
|
|||
|
||||
#pragma mark Named Structures
|
||||
|
||||
struct CGPoint {
|
||||
double _field1;
|
||||
double _field2;
|
||||
};
|
||||
|
||||
struct CGRect {
|
||||
struct CGPoint _field1;
|
||||
struct CGSize _field2;
|
||||
};
|
||||
|
||||
struct CGSize {
|
||||
double width;
|
||||
double height;
|
||||
};
|
||||
|
||||
struct PurpleFBMessage {
|
||||
struct {
|
||||
unsigned int _field1;
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
@property(readonly, nonatomic) unsigned long long rowByteSize; // @synthesize rowByteSize=_rowByteSize;
|
||||
@property(readonly, nonatomic) unsigned long long pixelsHigh; // @synthesize pixelsHigh=_pixelsHigh;
|
||||
@property(readonly, nonatomic) unsigned long long pixelsWide; // @synthesize pixelsWide=_pixelsWide;
|
||||
- (void).cxx_destruct;
|
||||
//- (void).cxx_destruct;
|
||||
@property(readonly, nonatomic) struct CGImage *image;
|
||||
- (void)flushDamageRegion:(struct CGRect)arg1;
|
||||
- (void)flushEntireLiveBuffer;
|
||||
- (void)accessBackingStoreDuring:(CDUnknownBlockType)arg1;
|
||||
- (void)accessBackingStoreDuring:(id)arg1;
|
||||
- (void)invalidate;
|
||||
- (id)initWithData:(void *)arg1 port:(unsigned int)arg2 size:(unsigned long long)arg3 rowByteSize:(unsigned long long)arg4 pixelsWide:(unsigned long long)arg5 pixelsHigh:(unsigned long long)arg6;
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
+ (id)framebufferServiceWithName:(id)arg1 device:(id)arg2 error:(id *)arg3;
|
||||
+ (id)mainScreenFramebufferServiceForDevice:(id)arg1 error:(id *)arg2;
|
||||
@property(readonly, nonatomic) NSObject<OS_dispatch_queue> *executionQueue; // @synthesize executionQueue=_executionQueue;
|
||||
- (void).cxx_destruct;
|
||||
- (void)requestDeviceDimensions:(struct CGSize)arg1 scaledDimensions:(struct CGSize)arg2;
|
||||
- (void)_ON_EXECUTION_QUEUE_didRotateToAngle:(double)arg1;
|
||||
- (void)_ON_EXECUTION_QUEUE_didDirtyFramebufferRegion:(struct CGRect)arg1;
|
||||
|
@ -34,7 +33,7 @@
|
|||
- (void)_ON_EXECUTION_QUEUE_teardownFramebufferBackingStore;
|
||||
- (void)_ON_RECEIVE_QUEUE_didCancelReceiveSource;
|
||||
- (void)_ON_RECEIVE_QUEUE_processMachMessage:(void *)arg1;
|
||||
- (BOOL)_ON_RECEIVE_QUEUE_sendReplyToRenderServer:(struct PurpleFBMessage *)arg1 error:(id *)arg2;
|
||||
- (BOOL)_ON_RECEIVE_QUEUE_sendReplyToRenderServer:(void *)arg1 error:(id *)arg2;
|
||||
- (void)_ON_RECEIVE_QUEUE_processMachMessages;
|
||||
- (void)_ON_EXECUTION_QUEUE_suspend;
|
||||
- (void)suspend;
|
||||
|
|
20
README.md
20
README.md
|
@ -90,10 +90,24 @@ To launch Safari on an iPhone 5, you can use the following:
|
|||
```
|
||||
|
||||
|
||||
## Multisim
|
||||
`FBSimulatorControl` launches Xcode's Simulator Applications directly, allowing specific Simulators to be targeted by UDID. `Simulator.app` uses a default set of Simulators located at `~/Library/Developer/CoreSimulator/Devices`. By passing arguments to the `Simulator.app` binary, `FBSimulatorControl` can launch Simulators from Device Sets other than the default. This allows multiple sets of Simulators to be booted from separate host processes, without interference.
|
||||
`FBSimulatorControl` currently has two ways of launching Simulators that have tradeoffs for different use cases. Since Xcode 7.2, both these methods can be used:
|
||||
|
||||
This is only supported on Xcode 7 or greater.
|
||||
## Multisim
|
||||
The `CoreSimulator` Framework that is used by the `Simulator.app` as well as Playgrounds & Interface Builder has long had the concept of custom 'Device Sets' which contain created Simulators. Multiple Device Sets can be used on the same host and are an effective way of ensuring that multiple processes using `CoreSimulator` don't collide into each other. 'Device Sets' are also beneficial for an automation use-case, as using a different set other than the 'Default' will ensure that these Simulators aren't polluted.
|
||||
|
||||
`CoreSimulator` itself is also capable of running multiple Simulators on the same host concurrently. You can see this for yourself by using the `simctl` commandline. Booting Simulators this way can be of somewhat limited utility without the output of the screen. `FBSimulatorControl` solves this problem in two different ways:
|
||||
|
||||
## Launching via `Simulator.app`
|
||||
`Simulator.app` is the Mac OS X Application bundle with Xcode that you are probably familiar with for viewing and interacting with a Simulator. This Mac Application is the part of the Xcode Toolchain that you will be used to.
|
||||
|
||||
`FBSimulatorControl` can launch the Application Excutable directly, thereby allowing specific Simulators to be booted by UDID and Device Set. This can be done by overriding the `Simulator.app`s `NSUserDefaults` by [passing them as Arguments to the Application Process](https://www.bignerdranch.com/blog/by-your-command). Once the Simulator has booted, it can be interacted with via `CoreSimulator` with commands such as installing Apps and launch executables.
|
||||
|
||||
There are however, a number of limitations to how much `FBSimulatorControl` can manipulate the Simulator, once it has been booted inside the `Simulator.app` process. In particular it's not [possible to execute custom code inside the Simulator Application process](https://gist.github.com/lawrencelomax/27bdc4e8a433a601008f), which means that it's not possible to get video frames that the booted simulator passes back to the `Simulator.app` process.
|
||||
|
||||
## Direct Launch
|
||||
`FBSimulatorControl` also supports 'Direct Launching'. This means that the Simulator is booted from the `FBSimulatorControl` Framework. This gives increasing control over the operation of the Simulator, including fetching frames from the Framebuffer. This means that pixel-perfect videos and screenshots can be constructed from the Framebuffer.
|
||||
|
||||
Direct Launching does not currently support manipulation of the UI within the Simulator, so is much better suited to a use-case where the [UI is manipulated by other means](https://github.com/facebook/webdriveragent).
|
||||
|
||||
## `fbsimctl`
|
||||
[`fbsimctl` is a Command Line Interface](https://github.com/facebook/FBSimulatorControl/blob/master/fbsimctl/README.md) for `FBSimulatorControl` API calls, so `FBSimulatorControl` functionality can be used without the need to integrate with the Framework. It is currently under development.
|
||||
|
|
|
@ -470,12 +470,13 @@ struct FBSimulatorConfigurationParser {
|
|||
struct FBSimulatorLaunchConfigurationParser {
|
||||
static func parser() -> Parser<FBSimulatorLaunchConfiguration> {
|
||||
return Parser
|
||||
.ofTwoSequenced(
|
||||
.ofThreeSequenced(
|
||||
self.localeParser().optional(),
|
||||
self.scaleParser().optional()
|
||||
self.scaleParser().optional(),
|
||||
self.optionsParser().optional()
|
||||
)
|
||||
.fmap { (locale, scale) in
|
||||
if locale == nil && scale == nil {
|
||||
.fmap { (locale, scale, options) in
|
||||
if locale == nil && scale == nil && options == nil {
|
||||
throw ParseError.Custom("Simulator Launch Configuration must contain at least a locale or scale")
|
||||
}
|
||||
var configuration = FBSimulatorLaunchConfiguration.defaultConfiguration().copy() as! FBSimulatorLaunchConfiguration
|
||||
|
@ -485,6 +486,9 @@ struct FBSimulatorLaunchConfigurationParser {
|
|||
if let scale = scale {
|
||||
configuration = configuration.withScale(scale)
|
||||
}
|
||||
if let options = options {
|
||||
configuration = configuration.withOptions(options)
|
||||
}
|
||||
return configuration
|
||||
}
|
||||
}
|
||||
|
@ -502,5 +506,14 @@ struct FBSimulatorLaunchConfigurationParser {
|
|||
Parser.ofString("--scale=100", FBSimulatorLaunchConfiguration_Scale_100())
|
||||
])
|
||||
}
|
||||
|
||||
static func optionsParser() -> Parser<FBSimulatorLaunchOptions> {
|
||||
return Parser<FBSimulatorLaunchOptions>
|
||||
.unionOptions(1, [
|
||||
Parser.ofString("--direct-launch", FBSimulatorLaunchOptions.EnableDirectLaunch),
|
||||
Parser.ofString("--record-video", FBSimulatorLaunchOptions.RecordVideo),
|
||||
Parser.ofString("--debug-window", FBSimulatorLaunchOptions.ShowDebugWindow)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,14 @@ public class EventSinkTranslator : NSObject, FBSimulatorEventSink {
|
|||
self.reportSimulator(EventName.Terminate, applicationProcess)
|
||||
}
|
||||
|
||||
public func framebufferDidStart(framebuffer: FBSimulatorFramebuffer!) {
|
||||
self.reportSimulator(EventName.Launch, framebuffer)
|
||||
}
|
||||
|
||||
public func framebufferDidTerminate(framebuffer: FBSimulatorFramebuffer!, expected: Bool) {
|
||||
self.reportSimulator(EventName.Terminate, framebuffer)
|
||||
}
|
||||
|
||||
public func simulatorDidLaunch(launchdSimProcess: FBProcessInfo!) {
|
||||
self.reportSimulator(EventName.Launch, launchdSimProcess)
|
||||
}
|
||||
|
|
|
@ -252,8 +252,12 @@ extension Parser {
|
|||
}
|
||||
|
||||
static func unionOptions<B : OptionSetType>(parsers: [Parser<B>]) -> Parser<B> {
|
||||
return Parser.unionOptions(0, parsers)
|
||||
}
|
||||
|
||||
static func unionOptions<B : OptionSetType>(count: Int, _ parsers: [Parser<B>]) -> Parser<B> {
|
||||
return Parser<B>
|
||||
.alternativeMany(parsers)
|
||||
.alternativeMany(count, parsers)
|
||||
.fmap { options in
|
||||
var set = B()
|
||||
for option in options {
|
||||
|
|
|
@ -154,6 +154,40 @@ class FBSimulatorConfigurationParserTests : XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
class FBSimulatorLaunchConfigurationTests : XCTestCase {
|
||||
func testParsesLocale() {
|
||||
self.assertParses(
|
||||
FBSimulatorLaunchConfigurationParser.parser(),
|
||||
["--locale", "fr_FR"],
|
||||
FBSimulatorLaunchConfiguration.defaultConfiguration().withLocaleNamed("fr_FR")
|
||||
)
|
||||
}
|
||||
|
||||
func testParsesScale() {
|
||||
self.assertParses(
|
||||
FBSimulatorLaunchConfigurationParser.parser(),
|
||||
["--scale=50"],
|
||||
FBSimulatorLaunchConfiguration.defaultConfiguration().scale50Percent()
|
||||
)
|
||||
}
|
||||
|
||||
func testParsesOptions() {
|
||||
self.assertParses(
|
||||
FBSimulatorLaunchConfigurationParser.parser(),
|
||||
["--record-video", "--direct-launch"],
|
||||
FBSimulatorLaunchConfiguration.defaultConfiguration().withOptions(FBSimulatorLaunchOptions.RecordVideo.union(FBSimulatorLaunchOptions.EnableDirectLaunch))
|
||||
)
|
||||
}
|
||||
|
||||
func testParsesAllTheAbove() {
|
||||
self.assertParses(
|
||||
FBSimulatorLaunchConfigurationParser.parser(),
|
||||
["--locale", "en_GB", "--scale=75", "--direct-launch","--record-video"],
|
||||
FBSimulatorLaunchConfiguration.defaultConfiguration().withLocaleNamed("en_GB").scale75Percent().withOptions(FBSimulatorLaunchOptions.RecordVideo.union(FBSimulatorLaunchOptions.EnableDirectLaunch))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigurationParserTests : XCTestCase {
|
||||
func testParsesEmptyAsDefaultValue() {
|
||||
self.assertParses(
|
||||
|
|
Загрузка…
Ссылка в новой задаче