diff --git a/CodePushDownloadHandler.m b/CodePushDownloadHandler.m index c56354b..31725d5 100644 --- a/CodePushDownloadHandler.m +++ b/CodePushDownloadHandler.m @@ -58,6 +58,9 @@ failCallback:(void (^)(NSError *err))failCallback { self.progressCallback(self.expectedContentLength, self.receivedContentLength); + // bytesLeft should not be negative. + assert(bytesLeft >= 0); + if (bytesLeft) { [self.outputFileStream close]; [connection cancel]; @@ -72,6 +75,9 @@ failCallback:(void (^)(NSError *err))failCallback { } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { + // We should have received all of the bytes if this is called. + assert(self.receivedContentLength == self.expectedContentLength); + [self.outputFileStream close]; self.doneCallback(); } diff --git a/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj b/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj index 0d3a903..3bab46c 100644 --- a/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj +++ b/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 5451ACBA1B86A5B600E2A7DF /* QueryUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */; }; 5451ACEC1B86E40A00E2A7DF /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5451ACEB1B86E34300E2A7DF /* libRCTTest.a */; }; 54D774BA1B87DAF800F2ABF8 /* ApplyUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D774B91B87DAF800F2ABF8 /* ApplyUpdateTests.m */; }; + 54F5F2B41BF6B45D007C3CEA /* DownloadProgressTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */; settings = {ASSET_TAGS = (); }; }; 81551E1B1B3B428000F5B9F1 /* libCodePush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81551E0F1B3B427200F5B9F1 /* libCodePush.a */; }; /* End PBXBuildFile section */ @@ -151,6 +152,7 @@ 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QueryUpdateTests.m; sourceTree = ""; }; 5451ACE61B86E34300E2A7DF /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = "node_modules/react-native/Libraries/RCTTest/RCTTest.xcodeproj"; sourceTree = ""; }; 54D774B91B87DAF800F2ABF8 /* ApplyUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApplyUpdateTests.m; sourceTree = ""; }; + 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadProgressTests.m; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 81551E0A1B3B427200F5B9F1 /* CodePush.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CodePush.xcodeproj; path = ../../CodePush.xcodeproj; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; @@ -238,6 +240,7 @@ 00E356EF1AD99517003FC87E /* CodePushDemoAppTests */ = { isa = PBXGroup; children = ( + 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */, 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */, 54D774B91B87DAF800F2ABF8 /* ApplyUpdateTests.m */, 00E356F01AD99517003FC87E /* Supporting Files */, @@ -608,6 +611,7 @@ buildActionMask = 2147483647; files = ( 5451ACBA1B86A5B600E2A7DF /* QueryUpdateTests.m in Sources */, + 54F5F2B41BF6B45D007C3CEA /* DownloadProgressTests.m in Sources */, 54D774BA1B87DAF800F2ABF8 /* ApplyUpdateTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests.m b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests.m new file mode 100644 index 0000000..fc23418 --- /dev/null +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests.m @@ -0,0 +1,36 @@ +#import +#import +#import + +#import "RCTAssert.h" + +#define FB_REFERENCE_IMAGE_DIR "\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\"" + +@interface DownloadProgressTests : XCTestCase + +@end + +@implementation DownloadProgressTests +{ + RCTTestRunner *_runner; +} + +- (void)setUp +{ +#if __LP64__ + RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)"); +#endif + + NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; + RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion); + _runner = RCTInitRunnerForApp(@"CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTestApp.ios", nil); +} + +#pragma mark Logic Tests +- (void)testDownloadProgress +{ + + [_runner runTest:_cmd module:@"DownloadProgressTest"]; +} + +@end diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTest.js new file mode 100644 index 0000000..f61a941 --- /dev/null +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTest.js @@ -0,0 +1,101 @@ +'use strict'; + +var RCTTestModule = require('NativeModules').TestModule; +var React = require('react-native'); +var CodePushSdk = require('react-native-code-push'); +var NativeBridge = require('react-native').NativeModules.CodePush; +var { NativeAppEventEmitter } = require("react-native"); + +var { + Text, + View, +} = React; + +var DownloadProgressTest = React.createClass({ + propTypes: { + shouldThrow: React.PropTypes.bool, + waitOneFrame: React.PropTypes.bool, + }, + + getInitialState() { + return { + done: false, + }; + }, + + componentDidMount() { + if (this.props.waitOneFrame) { + requestAnimationFrame(this.runTest); + } else { + this.runTest(); + } + }, + + checkReceivedAndExpectedBytesEqual() { + if (this.state.progress.receivedBytes !== this.state.progress.totalBytes) { + throw new Error("Bytes do not tally: Received bytes=" + this.state.progress.receivedBytes + " Total bytes=" + this.state.progress.totalBytes); + } + }, + + runTest() { + var downloadProgressSubscription = NativeAppEventEmitter.addListener( + "CodePushDownloadProgress", + (progress) => { + this.setState({ + progress:progress, + done: false, + }); + } + ); + + var updates = require("./TestPackages"); + NativeBridge.downloadUpdate(updates.smallPackage) + .then((smallPackage) => { + if (smallPackage) { + this.checkReceivedAndExpectedBytesEqual(); + return NativeBridge.downloadUpdate(updates.mediumPackage); + } else { + throw new Error("Small package download failed."); + } + }) + .then((mediumPackage) => { + if (mediumPackage) { + this.checkReceivedAndExpectedBytesEqual(); + return NativeBridge.downloadUpdate(updates.largePackage); + } else { + throw new Error("Medium package download failed."); + } + }) + .done((largePackage) => { + if (largePackage) { + this.checkReceivedAndExpectedBytesEqual(); + this.setState({done: true}, RCTTestModule.markTestCompleted); + } else { + throw new Error("Large package download failed."); + } + }); + }, + + render() { + var progressView; + if (this.state.progress) { + progressView = ( + {this.state.progress.receivedBytes} of {this.state.progress.totalBytes} bytes received + ); + } + + return ( + + + {this.constructor.displayName + ': '} + {this.state.done ? 'Done' : 'Testing...'} + + {progressView} + + ); + } +}); + +DownloadProgressTest.displayName = 'DownloadProgressTest'; + +module.exports = DownloadProgressTest; \ No newline at end of file diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTestApp.ios.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTestApp.ios.js new file mode 100644 index 0000000..9526b34 --- /dev/null +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTestApp.ios.js @@ -0,0 +1,79 @@ +'use strict'; + +var React = require('react-native'); + +var { + AppRegistry, + ScrollView, + StyleSheet, + Text, + TouchableOpacity, + View, +} = React; + +var TESTS = [ + require('./DownloadProgressTest') +]; + +TESTS.forEach( + (test) => AppRegistry.registerComponent(test.displayName, () => test) +); + +var DownloadProgressTestApp = React.createClass({ + getInitialState: function() { + return { + test: null, + }; + }, + render: function() { + if (this.state.test) { + return ( + + + + ); + } + return ( + + + Click on a test to run it in this shell for easier debugging and + development. Run all tests in the testing environment with cmd+U in + Xcode. + + + + {TESTS.map((test) => [ + this.setState({test})} + style={styles.row}> + + {test.displayName} + + , + + ])} + + + ); + } +}); + +var styles = StyleSheet.create({ + container: { + backgroundColor: 'white', + marginTop: 40, + margin: 15, + }, + row: { + padding: 10, + }, + testName: { + fontWeight: '500', + }, + separator: { + height: 1, + backgroundColor: '#bbbbbb', + } +}); + +AppRegistry.registerComponent('DownloadProgressTestApp', () => DownloadProgressTestApp); \ No newline at end of file diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/TestPackages.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/TestPackages.js new file mode 100644 index 0000000..dceefff --- /dev/null +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/TestPackages.js @@ -0,0 +1,35 @@ +module.exports = { + smallPackage: { + downloadUrl: "http://localhost:8081/CodePushDemoAppTests/DownloadProgressTests/smallFile", + description: "Angry flappy birds", + appVersion: "1.5.0", + label: "2.4.0", + isMandatory: false, + isAvailable: true, + updateAppVersion: false, + packageHash: "hash240", + packageSize: 1024 + }, + mediumPackage: { + downloadUrl: "http://localhost:8081/CodePushDemoAppTests/DownloadProgressTests/mediumFile", + description: "Angry flappy birds", + appVersion: "1.5.0", + label: "2.4.0", + isMandatory: false, + isAvailable: true, + updateAppVersion: false, + packageHash: "hash240", + packageSize: 1024 + }, + largePackage: { + downloadUrl: "http://localhost:8081/CodePushDemoAppTests/DownloadProgressTests/largeFile", + description: "Angry flappy birds", + appVersion: "1.5.0", + label: "2.4.0", + isMandatory: false, + isAvailable: true, + updateAppVersion: false, + packageHash: "hash240", + packageSize: 1024 + } +}; \ No newline at end of file diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/largeFile b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/largeFile new file mode 100644 index 0000000..1a20dba Binary files /dev/null and b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/largeFile differ diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/mediumFile b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/mediumFile new file mode 100644 index 0000000..3301331 Binary files /dev/null and b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/mediumFile differ diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/smallFile b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/smallFile new file mode 100644 index 0000000..da1dfb9 Binary files /dev/null and b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/smallFile differ