From 5f77c835a8cb30f41b6ce23587b094cd5c0ddbfe Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Fri, 10 Nov 2017 11:08:45 +0100 Subject: [PATCH 01/26] Set up node environment so version macthes user's shell --- src/RealmJS.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index 95b39927..bdc01843 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -888,7 +888,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "node ../scripts/download-realm.js ios --sync"; + shellScript = "if [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n . \"$HOME/.nvm/nvm.sh\"\n NVM_DIR=\"$HOME/.nvm\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n . \"$(brew --prefix nvm)/nvm.sh\"\n NVM_DIR=\"$(brew --prefix)\"\nfi\nnode ../scripts/download-realm.js ios --sync"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ From c4e17844967f1eae6ea159dd8754f6ec5ef56d28 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Mon, 13 Nov 2017 12:44:08 +0100 Subject: [PATCH 02/26] Better handling of local environment --- src/RealmJS.xcodeproj/project.pbxproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index bdc01843..d29df039 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -887,9 +887,8 @@ outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n . \"$HOME/.nvm/nvm.sh\"\n NVM_DIR=\"$HOME/.nvm\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n . \"$(brew --prefix nvm)/nvm.sh\"\n NVM_DIR=\"$(brew --prefix)\"\nfi\nnode ../scripts/download-realm.js ios --sync"; - showEnvVarsInLog = 0; + shellPath = "/bin/sh"; + shellScript = "[ -z \"$NVM_DIR\" ] && export NVM_DIR=\"$HOME/.nvm\"\n\nif [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n . \"$HOME/.nvm/nvm.sh\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n . \"$(brew --prefix nvm)/nvm.sh\"\nfi\nnode ../scripts/download-realm.js ios --sync"; }; /* End PBXShellScriptBuildPhase section */ From cc515a6a6059126e389982163555b70b3ac1e6f6 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Wed, 15 Nov 2017 11:59:13 +0100 Subject: [PATCH 03/26] Updating to RN 0.50.3. --- scripts/test.sh | 4 ++-- tests/js/admin-user-helper.js | 3 ++- tests/js/index.js | 3 ++- tests/js/session-tests.js | 3 ++- .../react-test-app/ios/ReactTests.xcodeproj/project.pbxproj | 2 +- tests/react-test-app/package.json | 5 +++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 0eea3e7d..766ed8bb 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -109,10 +109,10 @@ open_chrome() { start_packager() { watchman watch-del-all || true - ./node_modules/react-native/packager/packager.sh | tee "$PACKAGER_OUT" & + ./node_modules/react-native/scripts/packager.sh | tee "$PACKAGER_OUT" & while :; do - if grep -Fxq "React packager ready." "$PACKAGER_OUT"; then + if grep -Fxq "Metro Bundler ready." "$PACKAGER_OUT"; then break else echo "Waiting for packager." diff --git a/tests/js/admin-user-helper.js b/tests/js/admin-user-helper.js index e9eaa813..aadce944 100644 --- a/tests/js/admin-user-helper.js +++ b/tests/js/admin-user-helper.js @@ -1,6 +1,7 @@ 'use strict'; +const require_method = require; function node_require(module) { - return require(module); + return require_method(module); } const Realm = node_require('realm'); diff --git a/tests/js/index.js b/tests/js/index.js index 9b9514da..20fa6b5d 100644 --- a/tests/js/index.js +++ b/tests/js/index.js @@ -27,7 +27,8 @@ if( typeof Realm.Sync !== 'undefined' && Realm.Sync !== null ) { } const isNodeProcess = typeof process === 'object' && process + '' === '[object process]'; -function node_require(module) { return require(module); } +const require_method = require; +function node_require(module) { return require_method(module); } if (isNodeProcess && process.platform === 'win32') { global.enableSyncTests = false; diff --git a/tests/js/session-tests.js b/tests/js/session-tests.js index aab58bfb..c7859074 100644 --- a/tests/js/session-tests.js +++ b/tests/js/session-tests.js @@ -27,8 +27,9 @@ const TestCase = require('./asserts'); const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]'); +const require_method = require; function node_require(module) { - return require(module); + return require_method(module); } let tmp; diff --git a/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj b/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj index 774f755b..332547ce 100644 --- a/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj +++ b/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj @@ -829,7 +829,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/tests/react-test-app/package.json b/tests/react-test-app/package.json index 79c063fd..94d3533e 100644 --- a/tests/react-test-app/package.json +++ b/tests/react-test-app/package.json @@ -6,13 +6,14 @@ "start": "react-native start" }, "dependencies": { - "react": "~15.4.0-rc.4", - "react-native": "0.40.0", + "react": "~16.0.0-beta.5", + "react-native": "0.50.3", "react-native-fs": "^1.1.0", "realm": "file:../..", "realm-tests": "file:../js", "xmlbuilder": "^4.2.1" }, + "resolutions": { "moment": "2.19.1" }, "devDependencies": { "babel-preset-react-native": "1.9.1", "invariant": "^2.2.2" From 906a9dc34ef0cd9bf109a01fadeb08b1789971e3 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Thu, 16 Nov 2017 13:24:30 +0100 Subject: [PATCH 04/26] Download core/sync when testing --- scripts/test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test.sh b/scripts/test.sh index 766ed8bb..d4e864a6 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -292,6 +292,7 @@ case "$TARGET" in npm run check-environment download_server start_server + node ./scripts/download-realm.js ios --sync pushd tests/react-test-app npm install open_chrome @@ -303,6 +304,7 @@ case "$TARGET" in ;; "react-example") npm run check-environment + node ./scripts/download-realm.js ios --sync pushd examples/ReactExample npm install From 66d02b3e4e1d11b8312643e885a26622a4d9278f Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Thu, 16 Nov 2017 20:47:23 +0100 Subject: [PATCH 05/26] Don't download of sync during testing --- scripts/test.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index d4e864a6..766ed8bb 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -292,7 +292,6 @@ case "$TARGET" in npm run check-environment download_server start_server - node ./scripts/download-realm.js ios --sync pushd tests/react-test-app npm install open_chrome @@ -304,7 +303,6 @@ case "$TARGET" in ;; "react-example") npm run check-environment - node ./scripts/download-realm.js ios --sync pushd examples/ReactExample npm install From 92ab314ba5e40567f55d809ff602452305fb3f1e Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Fri, 17 Nov 2017 13:49:39 +0100 Subject: [PATCH 06/26] Updating packages in ReactExample --- .../ReactExample.xcodeproj/project.pbxproj | 2 +- examples/ReactExample/package.json | 47 ++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj b/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj index e16e57b5..ffc6720b 100644 --- a/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj +++ b/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj @@ -832,7 +832,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/examples/ReactExample/package.json b/examples/ReactExample/package.json index f9ec62e1..01fde7ba 100644 --- a/examples/ReactExample/package.json +++ b/examples/ReactExample/package.json @@ -1,24 +1,25 @@ { - "name": "ReactExample", - "version": "0.0.1", - "private": true, - "scripts": { - "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest" - }, - "dependencies": { - "react": "~15.4.0-rc.4", - "react-native": "0.40.0", - "realm": "file:../.." - }, - "devDependencies": { - "babel-jest": "18.0.0", - "babel-preset-react-native": "1.9.1", - "jest": "18.1.0", - "react-test-renderer": "~15.4.0-rc.4", - "invariant": "^2.2.2" - }, - "jest": { - "preset": "react-native" - } -} \ No newline at end of file + "name": "ReactExample", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "node node_modules/react-native/local-cli/cli.js start", + "test": "jest" + }, + "dependencies": { + "react": "~16.0.0-beta.5", + "react-native": "0.50.3", + "react-navigation": "^1.0.0-beta.19", + "realm": "file:../.." + }, + "devDependencies": { + "babel-jest": "18.0.0", + "babel-preset-react-native": "1.9.1", + "invariant": "^2.2.2", + "jest": "18.1.0", + "react-test-renderer": "^16.1.1" + }, + "jest": { + "preset": "react-native" + } +} From 7e8b7d7bab92bd475988d4a23dbe886ca0340912 Mon Sep 17 00:00:00 2001 From: blagoev Date: Mon, 4 Dec 2017 15:56:42 +0200 Subject: [PATCH 07/26] upgrade RN in test app + fix simulators handling + fix test app to run tests immediatelly and exit afterwards --- scripts/find-ios-runtime.rb | 9 + scripts/test.sh | 146 +++-------- tests/index.js | 4 +- tests/js/asserts.js | 7 +- tests/js/index.js | 2 +- tests/js/list-tests.js | 4 +- tests/js/results-tests.js | 48 ++-- tests/react-test-app/android/app/build.gradle | 2 + .../realm/react/testapp/MainApplication.java | 4 + tests/react-test-app/android/settings.gradle | 5 + tests/react-test-app/index.ios.js | 94 ++++++- .../ios/ReactTests.xcodeproj/project.pbxproj | 240 +++++++++++++++++- tests/react-test-app/package.json | 16 +- tests/react-test-app/tests/index.js | 8 +- 14 files changed, 436 insertions(+), 153 deletions(-) create mode 100644 scripts/find-ios-runtime.rb diff --git a/scripts/find-ios-runtime.rb b/scripts/find-ios-runtime.rb new file mode 100644 index 00000000..e2b8cfa1 --- /dev/null +++ b/scripts/find-ios-runtime.rb @@ -0,0 +1,9 @@ +#!/usr/bin/ruby + +require 'json' + +runtime = JSON.parse(%x{xcrun simctl list devices --json})['runtimes'] + .select{|x| (x['identifier'].include? 'com.apple.CoreSimulator.SimRuntime.iOS') && + (x['availability'] == "(available)")}[0]["identifier"] + +puts runtime diff --git a/scripts/test.sh b/scripts/test.sh index 766ed8bb..67286673 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -23,6 +23,8 @@ if [ -n "${JENKINS_HOME}" ]; then CI_RUN=true fi +SIM_DEVICE_NAME=realm-js-test + # Start current working directory at the root of the project. cd "$SRCROOT" @@ -97,7 +99,11 @@ cleanup() { fi } -open_chrome() { +open_chrome() { + if [ $CONFIGURATION == 'Release' ]; then + break + fi + local dir for dir in "$HOME/Applications" "/Applications"; do if [ -d "$dir/Google Chrome.app" ]; then @@ -124,122 +130,52 @@ start_packager() { xctest() { setup_ios_simulator - # - Wait until the simulator is fully booted by waiting for it to launch SpringBoard - printf "Waiting for springboard to ensure device is ready..." - xcrun simctl launch "$IOS_SIM_DEVICE" com.apple.springboard 1>/dev/null 2>/dev/null || true - echo " done" - # - Run the build and test - xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$IOS_SIM_DEVICE" build || { + echo "Building application" + xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="${IOS_SIM_DEVICE_ID}" -derivedDataPath ./build build || { EXITCODE=$? echo "*** Failure (exit code $EXITCODE). ***" exit $EXITCODE } - if [ -n "$XCPRETTY" ]; then - log_temp=$(mktemp build.log.XXXXXX) - if [ -e "$log_temp" ]; then - rm "$log_temp" - fi - xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination name="iPhone 5s" test 2>&1 | tee "$log_temp" | "$XCPRETTY" -c --no-utf --report junit --output build/reports/junit.xml || { - EXITCODE=$? - printf "*** Xcode Failure (exit code %s). The full xcode log follows: ***\n\n" "$EXITCODE" - cat "$log_temp" - printf "\n\n*** End Xcode Failure ***\n" - exit $EXITCODE - } - rm "$log_temp" + + echo "Installing application on ${SIM_DEVICE_NAME}" + echo "Application Path" $(pwd)/build/Build/Products/$CONFIGURATION-iphonesimulator/ReactTests.app + xcrun simctl install ${SIM_DEVICE_NAME} $(pwd)/build/Build/Products/$CONFIGURATION-iphonesimulator/ReactTests.app + + + echo "Launching application" + xcrun simctl launch --console ${SIM_DEVICE_NAME} io.realm.ReactTests | tee out.txt + + echo "Shuttting down ${SIM_DEVICE_NAME} simulator. (device is not deleted. you can use it to debug the app)" + xcrun simctl shutdown ${SIM_DEVICE_NAME} || true + + echo "Checking tests results" + if grep -q "REALM_FAILING_TESTS" out.txt; then + echo "*** REALM JS TESTS FAILED. See tests results above ***" + exit 20 else - xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$IOS_SIM_DEVICE" test || { - EXITCODE=$? - echo "*** Failure (exit code $EXITCODE). ***" - exit $EXITCODE - } + echo "*** REALM JS TESTS SUCCESS ***" fi } -setup_ios_simulator() { - # - Ensure one version of xcode is chosen by all tools - if [[ -z "$DEVELOPER_DIR" ]]; then - DEV_DIR="$(xcode-select -p)" - export DEVELOPER_DIR=$DEV_DIR - fi +setup_ios_simulator() { + shutdown_ios_simulator - # -- Ensure that the simulator is ready + #parse devices + IOS_RUNTIME=$(xcrun simctl list runtimes | grep -m1 -o '(com.apple.CoreSimulator.SimRuntime.iOS.*)' | sed 's/[()]//g') + echo using iOS Runtime ${IOS_RUNTIME} to create new simulator ${SIM_DEVICE_NAME} - if [ $CI_RUN == true ]; then - # - Kill the Simulator to ensure we are running the correct one, only when running in CI - echo "Resetting simulator using toolchain from: $DEVELOPER_DIR" + #create new test simulator + IOS_SIM_DEVICE_ID=$(xcrun simctl create ${SIM_DEVICE_NAME} com.apple.CoreSimulator.SimDeviceType.iPhone-SE ${IOS_RUNTIME}) + #boot new test simulator + xcrun simctl boot ${SIM_DEVICE_NAME} +} - # Quit Simulator.app to give it a chance to go down gracefully - local deadline=$((SECONDS+5)) - while pgrep -qx Simulator && [ $SECONDS -lt $deadline ]; do - osascript -e 'tell app "Simulator" to quit without saving' || true - sleep 0.25 # otherwise the pkill following will get it too early - done - - # stop CoreSimulatorService - launchctl remove com.apple.CoreSimulator.CoreSimulatorService 2>/dev/null || true - sleep 0.25 # launchtl can take a small moment to kill services - - # kill them with fire - while pgrep -qx Simulator com.apple.CoreSimulator.CoreSimulatorService; do - pkill -9 -x Simulator com.apple.CoreSimulator.CoreSimulatorService || true - sleep 0.05 - done - - # - Prod `simctl` a few times as sometimes it fails the first couple of times after switching XCode vesions - local deadline=$((SECONDS+5)) - while [ -z "$(xcrun simctl list devices 2>/dev/null)" ] && [ $SECONDS -lt $deadline ]; do - : # nothing to see here, will stop cycling on the first successful run - done - - # - Choose a device, if it has not already been chosen - local deadline=$((SECONDS+5)) - IOS_DEVICE="" - while [ -z "$IOS_DEVICE" ] && [ $SECONDS -lt $deadline ]; do - IOS_DEVICE="$(ruby $SRCROOT/scripts/find-ios-device.rb best)" - done - if [ -z "$IOS_DEVICE" ]; then - echo "*** Failed to determine the iOS Simulator device to use ***" - exit 1 - fi - export IOS_SIM_DEVICE=$IOS_DEVICE - - # - Reset the device we will be using if running in CI - xcrun simctl shutdown "$IOS_SIM_DEVICE" 1>/dev/null 2>/dev/null || true # sometimes simctl gets confused - xcrun simctl erase "$IOS_SIM_DEVICE" - - # - Start the target in Simulator.app - # Note: as of Xcode 7.3.1 `simctl` can not completely boot a simulator, specifically it can not bring up backboard, so GUI apps can not run. - # This is fixed in version 8 of Xcode, but we still need the compatibility - - "$DEVELOPER_DIR/Applications/Simulator.app/Contents/MacOS/Simulator" -CurrentDeviceUDID "$IOS_SIM_DEVICE" & # will get killed with all other children at exit - startedSimulator=true - - else - # - ensure that the simulator is running on a developer's workstation - open "$DEVELOPER_DIR/Applications/Simulator.app" - - # - Select the first device booted in the simulator, since it will boot something for us - local deadline=$((SECONDS+10)) - IOS_DEVICE="" - while [ -z "$IOS_DEVICE" ] && [ $SECONDS -lt $deadline ]; do - IOS_DEVICE="$(ruby $SRCROOT/scripts/find-ios-device.rb booted)" - done - if [ -z "$IOS_DEVICE" ]; then - echo "*** Failed to determine the iOS Simulator device in use ***" - exit 1 - fi - export IOS_SIM_DEVICE=$IOS_DEVICE - fi - - # Wait until the boot completes - printf " waiting for simulator (%s) to boot..." "$IOS_SIM_DEVICE" - until ruby -rjson -e "exit JSON.parse(%x{xcrun simctl list devices --json})['devices'].flat_map { |d| d[1] }.any? { |d| d['availability'] == '(available)' && d['state'] == 'Booted' }"; do - sleep 0.25 - done - echo " done" - echo "It will take some time before the simulator is fully ready, continuing on to other work" +shutdown_ios_simulator() { + #shutdown test simulator + xcrun simctl shutdown ${SIM_DEVICE_NAME} || true + #delete test simulator + xcrun simctl delete ${SIM_DEVICE_NAME} || true } # Cleanup now and also cleanup when this script exits. diff --git a/tests/index.js b/tests/index.js index 09fd9a9d..9db10452 100644 --- a/tests/index.js +++ b/tests/index.js @@ -56,7 +56,7 @@ function runTests() { return Object.keys(testNames).reduce((suitePromiseChain, suiteName) => { return suitePromiseChain.then(() => { - console.log('Starting ' + suiteName); + console.warn('Starting ' + suiteName); return testNames[suiteName].reduce((testPromiseChain, testName) => { return testPromiseChain.then(() => { @@ -64,7 +64,7 @@ function runTests() { }).then(() => { return RealmTests.runTest(suiteName, testName); }).then(() => { - console.log('+ ' + testName); + console.warn('+ ' + testName); }, (err) => { console.warn('- ' + testName); console.warn(err.message || err); diff --git a/tests/js/asserts.js b/tests/js/asserts.js index 1be734bc..ff01df46 100644 --- a/tests/js/asserts.js +++ b/tests/js/asserts.js @@ -203,7 +203,12 @@ module.exports = { }, assertType: function(value, type, depth) { - this.assertEqual(typeof value, type, `Value ${value} expected to be of type ${type}`, 1 + depth || 0); + try { + this.assertEqual(typeof value, type, "", 1 + depth || 0); + } + catch (e) { + throw new Error(`Value ${value} expected to be of type ${type}`) + } }, assertDefined: function(value, errorMessage, depth) { diff --git a/tests/js/index.js b/tests/js/index.js index 20fa6b5d..7aab4946 100644 --- a/tests/js/index.js +++ b/tests/js/index.js @@ -114,7 +114,7 @@ exports.runTest = function(suiteName, testName) { if (testMethod) { // Start fresh in case of a crash in a previous run. Realm.clearTestState(); - console.log("Starting test " + testName); + console.warn("Starting test " + testName); var promise; try { promise = testMethod.call(testSuite); diff --git a/tests/js/list-tests.js b/tests/js/list-tests.js index 145bc9ed..17d978d6 100644 --- a/tests/js/list-tests.js +++ b/tests/js/list-tests.js @@ -32,15 +32,13 @@ const DATE3 = new Date(3); module.exports = { testListConstructor: function() { const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]}); - realm.write(() => { let obj = realm.create('PersonList', {list: []}); TestCase.assertInstanceOf(obj.list, Realm.List); TestCase.assertInstanceOf(obj.list, Realm.Collection); }); - + TestCase.assertThrowsContaining(() => new Realm.List(), 'constructor'); - TestCase.assertType(Realm.List, 'function'); TestCase.assertInstanceOf(Realm.List, Function); }, diff --git a/tests/js/results-tests.js b/tests/js/results-tests.js index 9c238622..b6a74033 100644 --- a/tests/js/results-tests.js +++ b/tests/js/results-tests.js @@ -386,35 +386,36 @@ module.exports = { testResultsFindIndexOfObject: function() { var realm = new Realm({schema: [schemas.TestObject]}); - + var object1, object2, object3; realm.write(function() { object1 = realm.create('TestObject', {doubleCol: 1}); object2 = realm.create('TestObject', {doubleCol: 2}); object3 = realm.create('TestObject', {doubleCol: 2}); }); - + // Search in base table const objects = realm.objects('TestObject'); TestCase.assertEqual(objects.indexOf(object1), 0); TestCase.assertEqual(objects.indexOf(object2), 1); TestCase.assertEqual(objects.indexOf(object3), 2); - + // Search in filtered query const results = objects.filtered("doubleCol == 2"); TestCase.assertEqual(results.indexOf(object1), -1); TestCase.assertEqual(results.indexOf(object2), 0); TestCase.assertEqual(results.indexOf(object3), 1); - + const nonRealmObject = {test: "this is an object"}; TestCase.assertEqual(objects.indexOf(nonRealmObject), -1); - + // Searching for object from the wrong realm var realm2 = new Realm({path: '2.realm', schema: realm.schema}); var object4; realm2.write(function() { object4 = realm2.create('TestObject', {doubleCol: 1}); }); + TestCase.assertThrows(function() { objects.indexOf(object4); }); @@ -423,7 +424,7 @@ module.exports = { testAddListener: function() { if (typeof navigator !== 'undefined' && /Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef // FIXME: async callbacks do not work correctly in Chrome debugging mode - return; + return Promise.resolve(); } const realm = new Realm({ schema: [schemas.TestObject] }); @@ -433,29 +434,28 @@ module.exports = { realm.create('TestObject', { doubleCol: 3 }); }); - let resolve, first = true; + let resolve = () => {}; + let first = true; + + realm.objects('TestObject').addListener((testObjects, changes) => { + if (first) { + TestCase.assertEqual(testObjects.length, 3); + TestCase.assertEqual(changes.insertions.length, 0); + } + else { + TestCase.assertEqual(testObjects.length, 4); + TestCase.assertEqual(changes.insertions.length, 1); + } + first = false; + resolve(); + }); + return new Promise((r, _reject) => { - resolve = r; - realm.objects('TestObject').addListener((testObjects, changes) => { - if (first) { - TestCase.assertEqual(testObjects.length, 3); - TestCase.assertEqual(changes.insertions.length, 0); - } - else { - TestCase.assertEqual(testObjects.length, 4); - TestCase.assertEqual(changes.insertions.length, 1); - } - first = false; - resolve(); - }); - }).then(() => { - return new Promise((r, _reject) => { + resolve = r; realm.write(() => { realm.create('TestObject', { doubleCol: 1 }); }); - resolve = r; }); - }) }, testResultsAggregateFunctions: function() { diff --git a/tests/react-test-app/android/app/build.gradle b/tests/react-test-app/android/app/build.gradle index f060fc60..bec27402 100644 --- a/tests/react-test-app/android/app/build.gradle +++ b/tests/react-test-app/android/app/build.gradle @@ -137,6 +137,8 @@ android { } dependencies { + compile project(':react-native-exception-handler') + compile project(':react-native-exit-app-no-history') compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules diff --git a/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java b/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java index fc8092c2..977b654f 100644 --- a/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java +++ b/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java @@ -4,6 +4,8 @@ import android.app.Application; import android.util.Log; import com.facebook.react.ReactApplication; +import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage; +import com.github.wumke.RNExitApp.RNExitAppPackage; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; @@ -28,6 +30,8 @@ public class MainApplication extends Application implements ReactApplication { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new ReactNativeExceptionHandlerPackage(), + new RNExitAppPackage(), new RNFSPackage(), new RealmReactPackage() ); diff --git a/tests/react-test-app/android/settings.gradle b/tests/react-test-app/android/settings.gradle index 3157a0c9..98eee976 100644 --- a/tests/react-test-app/android/settings.gradle +++ b/tests/react-test-app/android/settings.gradle @@ -7,3 +7,8 @@ project(':realm').projectDir = new File(settingsDir, '../node_modules/realm/andr include ':react-native-fs' project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/react-native-fs/android') + +include ':react-native-exception-handler' +project(':react-native-exception-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exception-handler/android') +include ':react-native-exit-app-no-history' +project(':react-native-exit-app-no-history').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exit-app-no-history/android') diff --git a/tests/react-test-app/index.ios.js b/tests/react-test-app/index.ios.js index d1aaaa7b..f12ced2b 100644 --- a/tests/react-test-app/index.ios.js +++ b/tests/react-test-app/index.ios.js @@ -19,23 +19,109 @@ 'use strict'; import { + Alert, AppRegistry, StyleSheet, Text, View, } from 'react-native'; +import builder from 'xmlbuilder'; import React from 'react'; -import { runTests } from './tests'; +import { getTestNames, runTest } from './tests'; +import RNFS from 'react-native-fs'; +import RNExitApp from 'react-native-exit-app-no-history'; + +// import {setJSExceptionHandler} from 'react-native-exception-handler'; +// import {setNativeExceptionHandler} from 'react-native-exception-handler/index'; + +// setNativeExceptionHandler((exceptionString) => { +// console.error("\nRealm Tests App FAILED. NATIVE ERROR\n"); +// console.error(`\n${exceptionString}\n`); +// RNExitApp.exitApp(); +// }); + +// //unhandled JS exceptions handler +// setJSExceptionHandler((error, isFatal) => { +// console.error("\nRealm Tests App FAILED. JS ERROR\n"); +// console.error(`\n${error}\n`); +// RNExitApp.exitApp(); +// }); + + + + +async function runTests() { + try { + let testNames = getTestNames(); + let rootXml = builder.create('testsuites'); + let failingTests = []; + for (let suiteName in testNames) { + let itemTestsuite = rootXml.ele('testsuite'); + let nbrTests = 0; + let nbrFailures = 0; + + console.error('Starting ' + suiteName); + + for (let testName of testNames[suiteName]) { + nbrTests++; + + let itemTest = itemTestsuite.ele('testcase'); + itemTest.att('name', testName); + + try { + await runTest(suiteName, testName); + } + catch (e) { + failingTests.push(`${suiteName}: ${testName} : Error ${e.message}`); + itemTest.ele('error', {'message': e.message, 'stacktrace': e.stack}, e.toString()); + nbrFailures++; + } + } + + // update Junit XML report + itemTestsuite.att('name', suiteName); + itemTestsuite.att('tests', nbrTests); + itemTestsuite.att('failures', nbrFailures); + itemTestsuite.att('timestamp', "2016-01-22T14:40:44.874443-05:00");//TODO use real timestamp + + } + // export unit tests results + let xmlString = rootXml.end({ + pretty: true, + indent: ' ', + newline: '\n', + }); + + // write the unit tests reports + const path = RNFS.MainBundlePath + "/tests.xml"; + await RNFS.writeFile(path, xmlString, 'utf8'); + + //using console.log output is not shown in Release builds. using console.warn + console.warn(xmlString); + console.warn('__REALM_REACT_IOS_TESTS_COMPLETED__'); + if (failingTests.length !== 0) { + console.error('\n\nREALM_FAILING_TESTS\n'); + console.error(failingTests); + } + } + catch (e) { + console.error(e); + } + finally { + console.warn("Realm iOS Tests App finished. Exiting. Disable this to debug the app locally"); + RNExitApp.exitApp(); + } +} class ReactTests extends React.Component { render() { + runTests(); return ( - - Tap to Run Tests - + {'\n'}REALM-JS TESTS{'\n'} + Press Cmd+R to reload,{'\n'} Cmd+D or shake for dev menu diff --git a/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj b/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj index 332547ce..91b30d61 100644 --- a/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj +++ b/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj @@ -21,7 +21,9 @@ 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 1A31425AAD0B4731BDD7361C /* libReactNativeExceptionHandler.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BDA39DE0BF646F2AF670F18 /* libReactNativeExceptionHandler.a */; }; 1A9D2B80E7D649D2B5D8FE09 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E45185577984C00AA740BFE /* libz.tbd */; }; + 1C2471A6B2544BF3BD9D9C10 /* libRNExitApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DAA9FCB85294F77873D2769 /* libRNExitApp.a */; }; 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 66DA50ADC4F24D88856B9051 /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 629FEF95D64747E9A56D4D0C /* libRealmReact.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; @@ -31,6 +33,7 @@ 855301D31E2006F700FF108E /* RealmReactTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 855301D11E2006F400FF108E /* RealmReactTests.m */; }; A4CEF4BB1F7F862D00BA3B26 /* sync-v1.realm in Resources */ = {isa = PBXBuildFile; fileRef = A4CEF4BA1F7F862D00BA3B26 /* sync-v1.realm */; }; E2050A7A5BE14CEA9A9E0722 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B37A7097A134D5CBB4C462A /* libc++.tbd */; }; + FD7EF00801C34983A6188E6E /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EBE9BC51D4A4EF79B40A51A /* libRNFS.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -223,6 +226,90 @@ remoteGlobalIDString = F60690131CA2766F0003FB26; remoteInfo = RealmReact; }; + A474395F1FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D001F3B181A0099AA32; + remoteInfo = fishhook; + }; + A47439611FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; + remoteInfo = "fishhook-tvOS"; + }; + A47439711FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; + remoteInfo = "third-party"; + }; + A47439731FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; + remoteInfo = "third-party-tvOS"; + }; + A47439751FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7E881E25C6D100323FB7; + remoteInfo = "double-conversion"; + }; + A47439771FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D621EBD27B9005632C8; + remoteInfo = "double-conversion-tvOS"; + }; + A47439791FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; + remoteInfo = privatedata; + }; + A474397B1FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; + remoteInfo = "privatedata-tvOS"; + }; + A47439801FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC81504B0B8B47808E93B553 /* RNFS.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F12AFB9B1ADAF8F800E0535D; + remoteInfo = RNFS; + }; + A47439821FCF49A00034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC81504B0B8B47808E93B553 /* RNFS.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6456441F1EB8DA9100672408; + remoteInfo = "RNFS-tvOS"; + }; + A47439BA1FCF55E80034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5E54C8D078F84427A9DEE242 /* RNExitApp.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 15209BEF1D250F63000D0F44; + remoteInfo = RNExitApp; + }; + A47439F11FCF586E0034D32F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E65B8206DCD647B896F86688 /* ReactNativeExceptionHandler.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = ReactNativeExceptionHandler; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -245,8 +332,10 @@ 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 193A0C4F1D2C485DBE5ACC72 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RealmReact.xcodeproj; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = ""; }; 4E45185577984C00AA740BFE /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 5E54C8D078F84427A9DEE242 /* RNExitApp.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNExitApp.xcodeproj; path = "../node_modules/react-native-exit-app-no-history/ios/RNExitApp.xcodeproj"; sourceTree = ""; }; 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 629FEF95D64747E9A56D4D0C /* libRealmReact.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRealmReact.a; sourceTree = ""; }; + 6EBE9BC51D4A4EF79B40A51A /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 8553016A1E1FF6D500FF108E /* RealmJSTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmJSTests.mm; path = ../../ios/RealmJSTests.mm; sourceTree = ""; }; @@ -254,7 +343,11 @@ 855301CE1E20069D00FF108E /* dates-v5.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dates-v5.realm"; sourceTree = ""; }; 855301D11E2006F400FF108E /* RealmReactTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmReactTests.m; path = ReactTests/RealmReactTests.m; sourceTree = ""; }; 8B37A7097A134D5CBB4C462A /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + 9BDA39DE0BF646F2AF670F18 /* libReactNativeExceptionHandler.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libReactNativeExceptionHandler.a; sourceTree = ""; }; + 9DAA9FCB85294F77873D2769 /* libRNExitApp.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNExitApp.a; sourceTree = ""; }; A4CEF4BA1F7F862D00BA3B26 /* sync-v1.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "sync-v1.realm"; sourceTree = ""; }; + E65B8206DCD647B896F86688 /* ReactNativeExceptionHandler.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeExceptionHandler.xcodeproj; path = "../node_modules/react-native-exception-handler/ios/ReactNativeExceptionHandler.xcodeproj"; sourceTree = ""; }; + FC81504B0B8B47808E93B553 /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -284,6 +377,9 @@ 66DA50ADC4F24D88856B9051 /* libRealmReact.a in Frameworks */, E2050A7A5BE14CEA9A9E0722 /* libc++.tbd in Frameworks */, 1A9D2B80E7D649D2B5D8FE09 /* libz.tbd in Frameworks */, + FD7EF00801C34983A6188E6E /* libRNFS.a in Frameworks */, + 1C2471A6B2544BF3BD9D9C10 /* libRNExitApp.a in Frameworks */, + 1A31425AAD0B4731BDD7361C /* libReactNativeExceptionHandler.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -346,6 +442,8 @@ children = ( 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, + A47439601FCF49A00034D32F /* libfishhook.a */, + A47439621FCF49A00034D32F /* libfishhook-tvOS.a */, ); name = Products; sourceTree = ""; @@ -375,6 +473,12 @@ 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, + A47439721FCF49A00034D32F /* libthird-party.a */, + A47439741FCF49A00034D32F /* libthird-party.a */, + A47439761FCF49A00034D32F /* libdouble-conversion.a */, + A47439781FCF49A00034D32F /* libdouble-conversion.a */, + A474397A1FCF49A00034D32F /* libprivatedata.a */, + A474397C1FCF49A00034D32F /* libprivatedata-tvOS.a */, ); name = Products; sourceTree = ""; @@ -400,7 +504,7 @@ isa = PBXGroup; children = ( 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, - 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */, + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, ); name = Products; sourceTree = ""; @@ -429,6 +533,9 @@ 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 193A0C4F1D2C485DBE5ACC72 /* RealmReact.xcodeproj */, + FC81504B0B8B47808E93B553 /* RNFS.xcodeproj */, + 5E54C8D078F84427A9DEE242 /* RNExitApp.xcodeproj */, + E65B8206DCD647B896F86688 /* ReactNativeExceptionHandler.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -486,6 +593,31 @@ path = ../../data; sourceTree = ""; }; + A474394A1FCF49A00034D32F /* Products */ = { + isa = PBXGroup; + children = ( + A47439811FCF49A00034D32F /* libRNFS.a */, + A47439831FCF49A00034D32F /* libRNFS.a */, + ); + name = Products; + sourceTree = ""; + }; + A47439951FCF55E80034D32F /* Products */ = { + isa = PBXGroup; + children = ( + A47439BB1FCF55E80034D32F /* libRNExitApp.a */, + ); + name = Products; + sourceTree = ""; + }; + A47439CD1FCF586E0034D32F /* Products */ = { + isa = PBXGroup; + children = ( + A47439F21FCF586E0034D32F /* libReactNativeExceptionHandler.a */, + ); + name = Products; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -596,10 +728,22 @@ ProductGroup = 146834001AC3E56700842450 /* Products */; ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; }, + { + ProductGroup = A47439CD1FCF586E0034D32F /* Products */; + ProjectRef = E65B8206DCD647B896F86688 /* ReactNativeExceptionHandler.xcodeproj */; + }, { ProductGroup = 855301781E1FF78600FF108E /* Products */; ProjectRef = 193A0C4F1D2C485DBE5ACC72 /* RealmReact.xcodeproj */; }, + { + ProductGroup = A47439951FCF55E80034D32F /* Products */; + ProjectRef = 5E54C8D078F84427A9DEE242 /* RNExitApp.xcodeproj */; + }, + { + ProductGroup = A474394A1FCF49A00034D32F /* Products */; + ProjectRef = FC81504B0B8B47808E93B553 /* RNFS.xcodeproj */; + }, ); projectRoot = ""; targets = ( @@ -764,10 +908,10 @@ remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */ = { + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libRCTAnimation-tvOS.a"; + path = libRCTAnimation.a; remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -792,6 +936,90 @@ remoteRef = 855301941E1FF78600FF108E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + A47439601FCF49A00034D32F /* libfishhook.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libfishhook.a; + remoteRef = A474395F1FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439621FCF49A00034D32F /* libfishhook-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libfishhook-tvOS.a"; + remoteRef = A47439611FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439721FCF49A00034D32F /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = A47439711FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439741FCF49A00034D32F /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = A47439731FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439761FCF49A00034D32F /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = A47439751FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439781FCF49A00034D32F /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = A47439771FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A474397A1FCF49A00034D32F /* libprivatedata.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libprivatedata.a; + remoteRef = A47439791FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A474397C1FCF49A00034D32F /* libprivatedata-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libprivatedata-tvOS.a"; + remoteRef = A474397B1FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439811FCF49A00034D32F /* libRNFS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNFS.a; + remoteRef = A47439801FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439831FCF49A00034D32F /* libRNFS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNFS.a; + remoteRef = A47439821FCF49A00034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439BB1FCF55E80034D32F /* libRNExitApp.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNExitApp.a; + remoteRef = A47439BA1FCF55E80034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A47439F21FCF586E0034D32F /* libReactNativeExceptionHandler.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReactNativeExceptionHandler.a; + remoteRef = A47439F11FCF586E0034D32F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -889,6 +1117,9 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactTests.app/ReactTests"; @@ -907,6 +1138,9 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactTests.app/ReactTests"; diff --git a/tests/react-test-app/package.json b/tests/react-test-app/package.json index 94d3533e..9bfbd4a8 100644 --- a/tests/react-test-app/package.json +++ b/tests/react-test-app/package.json @@ -3,19 +3,23 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "react-native start" + "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { - "react": "~16.0.0-beta.5", - "react-native": "0.50.3", - "react-native-fs": "^1.1.0", + "react": "16.0.0", + "react-native": "0.50.4", + "react-native-exception-handler": "^2.4.3", + "react-native-exit-app-no-history": "^1.0.2", + "react-native-fs": "^2.8.5", "realm": "file:../..", "realm-tests": "file:../js", "xmlbuilder": "^4.2.1" }, - "resolutions": { "moment": "2.19.1" }, + "resolutions": { + "moment": "2.19.1" + }, "devDependencies": { - "babel-preset-react-native": "1.9.1", + "babel-preset-react-native": "4.0.0", "invariant": "^2.2.2" } } diff --git a/tests/react-test-app/tests/index.js b/tests/react-test-app/tests/index.js index ebaabcba..9e3d1673 100644 --- a/tests/react-test-app/tests/index.js +++ b/tests/react-test-app/tests/index.js @@ -52,7 +52,7 @@ export async function runTests() { let passed = true; for (let suiteName in testNames) { - console.log('Starting ' + suiteName); + console.warn('Starting ' + suiteName); for (let testName of testNames[suiteName]) { try { @@ -72,11 +72,11 @@ export async function runTest(suiteName, testName) { try { await RealmTests.runTest(suiteName, testName); - console.log('+ ' + testName); + console.warn('+ ' + testName); } catch (e) { - console.warn('- ' + testName); - console.warn(e.message || e); + console.error('- ' + testName); + console.error(e.message || e); throw e; } finally { From b842d07726c88010403b56e1d50ceedd2dc6049d Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 5 Dec 2017 14:46:41 +0200 Subject: [PATCH 08/26] remove explict ROS uninstall --- scripts/download-object-server.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/download-object-server.sh b/scripts/download-object-server.sh index bca57c0a..b96b495c 100755 --- a/scripts/download-object-server.sh +++ b/scripts/download-object-server.sh @@ -9,15 +9,11 @@ set -eo pipefail #use existing server if same version if [ -f node_modules/realm-object-server/package.json ]; then if grep -q "\"version\": \"$REALM_OBJECT_SERVER_VERSION\"" node_modules/realm-object-server/package.json; then - # echo -e "yes\n" | object-server-for-testing/reset-server-realms.command rm -rf realm-object-server-data rm -rf realm-object-server exit fi fi -echo "Uninstalling old version of realm-object-server" -npm uninstall realm-object-server - echo "Installing realm-object-server version: " $REALM_OBJECT_SERVER_VERSION npm install realm-object-server@$REALM_OBJECT_SERVER_VERSION \ No newline at end of file From 36a6f44bbdba289613b5ee39f6e02ed7e2468378 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 5 Dec 2017 14:47:08 +0200 Subject: [PATCH 09/26] ignore test output file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6f82ef03..779d6a36 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ tests/realm-object-server/ packager_out.txt build.log.* realm-object-server/ +tests/react-test-app/ios/out.txt From 9084c2d271f4f9eac104566fd4e1a9c611d5f26a Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 5 Dec 2017 14:47:44 +0200 Subject: [PATCH 10/26] depend on lzma-native to fix lzma-native native module not found --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f024f896..c2ed7ef1 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,8 @@ "request": "^2.78.0", "stream-counter": "^1.0.0", "sync-request": "^3.0.1", - "url-parse": "^1.1.7" + "url-parse": "^1.1.7", + "lzma-native": "^3.0.4" }, "devDependencies": { "babel-eslint": "^6.0.4", From bb97ea265384c030dc282e489c0da7cf614e2120 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 5 Dec 2017 17:48:15 +0200 Subject: [PATCH 11/26] use consistent node version --- src/RealmJS.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index d29df039..032de015 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -888,7 +888,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/bin/sh"; - shellScript = "[ -z \"$NVM_DIR\" ] && export NVM_DIR=\"$HOME/.nvm\"\n\nif [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n . \"$HOME/.nvm/nvm.sh\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n . \"$(brew --prefix nvm)/nvm.sh\"\nfi\nnode ../scripts/download-realm.js ios --sync"; + shellScript = "[ -z \"$NVM_DIR\" ] && export NVM_DIR=\"$HOME/.nvm\"\n\nif [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n . \"$HOME/.nvm/nvm.sh\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n . \"$(brew --prefix nvm)/nvm.sh\"\nfi\n \nif [[ \"$(command -v nvm)\" ]]; then\n nvm install 7.10.0\nfi\n \n node ../scripts/download-realm.js ios --sync"; }; /* End PBXShellScriptBuildPhase section */ From 3000e5d11799befc56189c7ca9594885963539e9 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 6 Dec 2017 14:42:20 +0200 Subject: [PATCH 12/26] rename test application Xcode scheme --- tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj | 2 ++ .../xcschemes/{ReactTestApp.xcscheme => ReactTests.xcscheme} | 0 2 files changed, 2 insertions(+) rename tests/react-test-app/ios/ReactTests.xcodeproj/xcshareddata/xcschemes/{ReactTestApp.xcscheme => ReactTests.xcscheme} (100%) diff --git a/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj b/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj index 91b30d61..7ef46e59 100644 --- a/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj +++ b/tests/react-test-app/ios/ReactTests.xcodeproj/project.pbxproj @@ -1155,6 +1155,7 @@ INFOPLIST_FILE = ReactTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = io.realm.ReactTests; PRODUCT_NAME = ReactTests; }; name = Debug; @@ -1166,6 +1167,7 @@ INFOPLIST_FILE = ReactTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = io.realm.ReactTests; PRODUCT_NAME = ReactTests; }; name = Release; diff --git a/tests/react-test-app/ios/ReactTests.xcodeproj/xcshareddata/xcschemes/ReactTestApp.xcscheme b/tests/react-test-app/ios/ReactTests.xcodeproj/xcshareddata/xcschemes/ReactTests.xcscheme similarity index 100% rename from tests/react-test-app/ios/ReactTests.xcodeproj/xcshareddata/xcschemes/ReactTestApp.xcscheme rename to tests/react-test-app/ios/ReactTests.xcodeproj/xcshareddata/xcschemes/ReactTests.xcscheme From 18a63d6f79dd8f190f5f7046a77c91d21a230401 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 6 Dec 2017 14:43:02 +0200 Subject: [PATCH 13/26] allow multiple test applications to be run (identified by application scheme.) --- scripts/test.sh | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 67286673..96c29ba0 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -77,6 +77,9 @@ cleanup() { # Kill started object server stop_server || true + echo "shutting down running simulators" + shutdown_ios_simulator >/dev/null 2>&1 + # Quit Simulator.app to give it a chance to go down gracefully if $startedSimulator; then osascript -e 'tell app "Simulator" to quit without saving' || true @@ -139,27 +142,29 @@ xctest() { } echo "Installing application on ${SIM_DEVICE_NAME}" - echo "Application Path" $(pwd)/build/Build/Products/$CONFIGURATION-iphonesimulator/ReactTests.app - xcrun simctl install ${SIM_DEVICE_NAME} $(pwd)/build/Build/Products/$CONFIGURATION-iphonesimulator/ReactTests.app + echo "Application Path" $(pwd)/build/Build/Products/$CONFIGURATION-iphonesimulator/$1.app + xcrun simctl install ${SIM_DEVICE_NAME} $(pwd)/build/Build/Products/$CONFIGURATION-iphonesimulator/$1.app - echo "Launching application" - xcrun simctl launch --console ${SIM_DEVICE_NAME} io.realm.ReactTests | tee out.txt + echo "Launching application. (output is in $(pwd)/build/out.txt)" + xcrun simctl launch --console ${SIM_DEVICE_NAME} io.realm.$1 | tee $(pwd)/build/out.txt echo "Shuttting down ${SIM_DEVICE_NAME} simulator. (device is not deleted. you can use it to debug the app)" - xcrun simctl shutdown ${SIM_DEVICE_NAME} || true + shutdown_ios_simulator echo "Checking tests results" - if grep -q "REALM_FAILING_TESTS" out.txt; then + if grep -q "REALM_FAILING_TESTS" $(pwd)/build/out.txt; then echo "*** REALM JS TESTS FAILED. See tests results above ***" exit 20 else - echo "*** REALM JS TESTS SUCCESS ***" + echo "*** $1 SUCCESS ***" fi } setup_ios_simulator() { - shutdown_ios_simulator + #try deleting old simulator with same name. + echo "Preparing to create a new simulator" + delete_ios_simulator >/dev/null 2>&1 #parse devices IOS_RUNTIME=$(xcrun simctl list runtimes | grep -m1 -o '(com.apple.CoreSimulator.SimRuntime.iOS.*)' | sed 's/[()]//g') @@ -174,12 +179,17 @@ setup_ios_simulator() { shutdown_ios_simulator() { #shutdown test simulator xcrun simctl shutdown ${SIM_DEVICE_NAME} || true +} + +delete_ios_simulator() { + shutdown_ios_simulator + #delete test simulator xcrun simctl delete ${SIM_DEVICE_NAME} || true } # Cleanup now and also cleanup when this script exits. -cleanup +cleanup >/dev/null 2>&1 trap cleanup EXIT # Use a consistent version of Node if possible. @@ -234,7 +244,7 @@ case "$TARGET" in start_packager pushd ios - xctest ReactTestApp + xctest ReactTests stop_server ;; "react-example") From 9f41664309e9cad934a0d6a9a1ecfc093d9f46dc Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 6 Dec 2017 14:44:00 +0200 Subject: [PATCH 14/26] try fix ReactExample test app --- examples/ReactExample/components/styles.js | 5 +++-- examples/ReactExample/components/todo-app.js | 8 +++++--- examples/ReactExample/package.json | 9 +++++---- .../Images.xcassets/AppIcon.appiconset/Contents.json | 10 ++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/examples/ReactExample/components/styles.js b/examples/ReactExample/components/styles.js index 05c01a87..67c5d11b 100644 --- a/examples/ReactExample/components/styles.js +++ b/examples/ReactExample/components/styles.js @@ -19,14 +19,15 @@ 'use strict'; import { - Navigator, Platform, StyleSheet } from 'react-native'; +import NavigationExperimental from 'react-native-deprecated-custom-components'; -const { NavBarHeight, TotalNavHeight } = Navigator.NavigationBar.Styles.General; +const { NavBarHeight, TotalNavHeight } = NavigationExperimental.Navigator.NavigationBar.Styles.General; const iOS = (Platform.OS == 'ios'); + export default StyleSheet.create({ container: { flex: 1, diff --git a/examples/ReactExample/components/todo-app.js b/examples/ReactExample/components/todo-app.js index d2ff4fe4..e51b62e9 100644 --- a/examples/ReactExample/components/todo-app.js +++ b/examples/ReactExample/components/todo-app.js @@ -21,7 +21,6 @@ import React from 'react'; import { - Navigator, Platform, StatusBar, Text, @@ -34,6 +33,9 @@ import TodoListView from './todo-listview'; import realm from './realm'; import styles from './styles'; +import NavigationExperimental from 'react-native-deprecated-custom-components'; + + export default class TodoApp extends React.Component { constructor(props) { super(props); @@ -91,11 +93,11 @@ export default class TodoApp extends React.Component { }; let navigationBar = ( - + ); return ( - Date: Wed, 13 Dec 2017 18:36:10 +0200 Subject: [PATCH 15/26] fix RealmExample app to use the new RN navigation --- .../ReactExample/android/app/build.gradle | 1 + .../realm/react/example/MainApplication.java | 2 + examples/ReactExample/android/settings.gradle | 2 + .../ReactExample/components/items-screen.js | 104 +++++++++++ examples/ReactExample/components/params.json | 1 + examples/ReactExample/components/styles.js | 8 +- examples/ReactExample/components/todo-app.js | 133 ++++---------- .../ReactExample/components/todo-itemsview.js | 172 ++++++++++++++++++ .../ReactExample/components/todo-list-item.js | 7 +- .../ReactExample/components/todo-listview.js | 2 +- .../ReactExample.xcodeproj/project.pbxproj | 8 +- examples/ReactExample/package.json | 4 +- scripts/test.sh | 3 + 13 files changed, 340 insertions(+), 107 deletions(-) create mode 100644 examples/ReactExample/components/items-screen.js create mode 100644 examples/ReactExample/components/params.json create mode 100644 examples/ReactExample/components/todo-itemsview.js diff --git a/examples/ReactExample/android/app/build.gradle b/examples/ReactExample/android/app/build.gradle index 9ecce5be..a9b4e033 100644 --- a/examples/ReactExample/android/app/build.gradle +++ b/examples/ReactExample/android/app/build.gradle @@ -126,6 +126,7 @@ android { } dependencies { + compile project(':react-native-exit-app-no-history') compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules diff --git a/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java b/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java index fd45f4fc..b220a0d8 100644 --- a/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java +++ b/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java @@ -4,6 +4,7 @@ import android.app.Application; import android.util.Log; import com.facebook.react.ReactApplication; +import com.github.wumke.RNExitApp.RNExitAppPackage; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; @@ -27,6 +28,7 @@ public class MainApplication extends Application implements ReactApplication { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new RNExitAppPackage(), new RealmReactPackage() ); } diff --git a/examples/ReactExample/android/settings.gradle b/examples/ReactExample/android/settings.gradle index 96ce9242..28f31ada 100644 --- a/examples/ReactExample/android/settings.gradle +++ b/examples/ReactExample/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'ReactExample' +include ':react-native-exit-app-no-history' +project(':react-native-exit-app-no-history').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exit-app-no-history/android') include ':app' diff --git a/examples/ReactExample/components/items-screen.js b/examples/ReactExample/components/items-screen.js new file mode 100644 index 00000000..02d2f095 --- /dev/null +++ b/examples/ReactExample/components/items-screen.js @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +'use strict'; + +import React from 'react'; + +import { + Platform, + StatusBar, + Text, + TouchableOpacity, + View, +} from 'react-native'; + +import TodoItem from './todo-item'; +import TodoListView from './todo-listview'; +import TodoItemsView from './todo-itemsview'; +import TodoListItem from './todo-list-item'; +import realm from './realm'; +import styles from './styles'; + +import { StackNavigator } from 'react-navigation'; + +export default class ItemsScreen extends React.Component { + static navigationOptions = { + title: 'Current list', + }; + + constructor(props) { + super(props); + this.state = {}; + } + + componentWillMount() { + if (Platform.OS == 'ios') { + StatusBar.setBarStyle('light-content'); + } + } + + render() { + // let objects = realm.objects('Todo'); + // let extraItems = [ + // { name: 'Complete', items: objects.filtered('done = true') }, + // { name: 'Incomplete', items: objects.filtered('done = false') }, + // ]; + + let properties = { + // ref: 'listView', + // extraItems: extraItems, + // onPressItem: this._onPressTodoList, + } + + return ; + } + + // renderScene(route) { + // console.log(this.todoLists); + // return + // } + + + + // _onPressTodoItem(list) { + // const { navigate } = this.props.navigation; + // let items = list.items; + + // let route = { + // title: list.name, + // component: TodoListView, + // passProps: { + // ref: 'listItemView', + // items: items, + // rowClass: TodoItem, + // }, + // }; + + // // Check if the items are mutable (i.e. List rather than Results). + // if (items.push) { + // Object.assign(route, { + // rightButtonTitle: 'Add', + // onRightButtonPress: () => this._addNewTodoItem(list), + // }); + // } + + // // this.refs.nav.push(route); + // navigate('TodoListItem', { items: items }) + // } +} \ No newline at end of file diff --git a/examples/ReactExample/components/params.json b/examples/ReactExample/components/params.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/examples/ReactExample/components/params.json @@ -0,0 +1 @@ +{} diff --git a/examples/ReactExample/components/styles.js b/examples/ReactExample/components/styles.js index 67c5d11b..32f8f00b 100644 --- a/examples/ReactExample/components/styles.js +++ b/examples/ReactExample/components/styles.js @@ -22,9 +22,9 @@ import { Platform, StyleSheet } from 'react-native'; -import NavigationExperimental from 'react-native-deprecated-custom-components'; +// import NavigationExperimental from 'react-native-deprecated-custom-components'; -const { NavBarHeight, TotalNavHeight } = NavigationExperimental.Navigator.NavigationBar.Styles.General; +// const { NavBarHeight, TotalNavHeight } = NavigationExperimental.Navigator.NavigationBar.Styles.General; const iOS = (Platform.OS == 'ios'); @@ -44,7 +44,7 @@ export default StyleSheet.create({ navBarView: { alignItems: 'center', flexDirection: 'row', - height: NavBarHeight, + height: 20, }, navBarLeftArrow: { color: '#fff', @@ -67,7 +67,7 @@ export default StyleSheet.create({ fontWeight: '500', }, navScene: { - top: TotalNavHeight, + top: 20, }, listItem: { borderColor: '#c8c7cc', diff --git a/examples/ReactExample/components/todo-app.js b/examples/ReactExample/components/todo-app.js index e51b62e9..cd8b4bd6 100644 --- a/examples/ReactExample/components/todo-app.js +++ b/examples/ReactExample/components/todo-app.js @@ -30,13 +30,22 @@ import { import TodoItem from './todo-item'; import TodoListView from './todo-listview'; +import TodoListItem from './todo-list-item'; +import ItemsScreen from './items-screen' import realm from './realm'; import styles from './styles'; -import NavigationExperimental from 'react-native-deprecated-custom-components'; +// import NavigationExperimental from 'react-native-deprecated-custom-components'; +import { StackNavigator } from 'react-navigation'; +import RNExitApp from 'react-native-exit-app-no-history'; +const params = require("./params.json"); + +class HomeScreen extends React.Component { + static navigationOptions = { + title: 'Todo Lists', + }; -export default class TodoApp extends React.Component { constructor(props) { super(props); @@ -44,17 +53,21 @@ export default class TodoApp extends React.Component { this.todoLists = realm.objects('TodoList').sorted('creationDate'); if (this.todoLists.length < 1) { realm.write(() => { - realm.create('TodoList', {name: 'Todo List', creationDate: new Date()}); + realm.create('TodoList', { name: 'Todo List', creationDate: new Date() }); }); } this.todoLists.addListener((name, changes) => { console.log("changed: " + JSON.stringify(changes)); + if (params) { + console.error("params.json indicate a test run. Exiting application"); + RNExitApp.exitApp(); + } }); console.log("registered listener"); // Bind all the methods that we will be passing as props. - this.renderScene = this.renderScene.bind(this); + // this.renderScene = this.renderScene.bind(this); this._addNewTodoList = this._addNewTodoList.bind(this); this._onPressTodoList = this._onPressTodoList.bind(this); @@ -75,37 +88,17 @@ export default class TodoApp extends React.Component { render() { let objects = realm.objects('Todo'); let extraItems = [ - {name: 'Complete', items: objects.filtered('done = true')}, - {name: 'Incomplete', items: objects.filtered('done = false')}, + { name: 'Complete', items: objects.filtered('done = true') }, + { name: 'Incomplete', items: objects.filtered('done = false') }, ]; - let route = { - title: 'My Todo Lists', - component: TodoListView, - passProps: { - ref: 'listView', - extraItems: extraItems, - onPressItem: this._onPressTodoList, - }, - backButtonTitle: 'Lists', - rightButtonTitle: 'Add', - onRightButtonPress: this._addNewTodoList, - }; + let properties = { + ref: 'listView', + extraItems: extraItems, + onPressItem: this._onPressTodoList, + } - let navigationBar = ( - - ); - - return ( - - ); + return ; } renderScene(route) { @@ -120,7 +113,7 @@ export default class TodoApp extends React.Component { } realm.write(() => { - items.push({text: ''}); + items.push({ text: '' }); }); this._setEditingRow(items.length - 1); @@ -133,34 +126,17 @@ export default class TodoApp extends React.Component { } realm.write(() => { - realm.create('TodoList', {name: '', creationDate: new Date()}); + realm.create('TodoList', { name: '', creationDate: new Date() }); }); this._setEditingRow(items.length - 1); } _onPressTodoList(list) { + const { navigate } = this.props.navigation; let items = list.items; - let route = { - title: list.name, - component: TodoListView, - passProps: { - ref: 'listItemView', - items: items, - rowClass: TodoItem, - }, - }; - - // Check if the items are mutable (i.e. List rather than Results). - if (items.push) { - Object.assign(route, { - rightButtonTitle: 'Add', - onRightButtonPress: () => this._addNewTodoItem(list), - }); - } - - this.refs.nav.push(route); + navigate('ItemsScreen', { items: items }) } _shouldAddNewItem(items) { @@ -175,53 +151,14 @@ export default class TodoApp extends React.Component { let listView = this.currentListView; // Update the state on the currently displayed TodoList to edit this new item. - listView.setState({editingRow: rowIndex}); + listView.setState({ editingRow: rowIndex }); listView.updateDataSource(); } } -const RouteMapper = { - LeftButton(route, navigator, index, navState) { - if (index == 0) { - return null; - } +const SimpleApp = StackNavigator({ + Home: { screen: HomeScreen }, + ItemsScreen: { screen: ItemsScreen } +}); - let prevRoute = navState.routeStack[index - 1]; - return ( - navigator.pop()}> - - - - {prevRoute.backButtonTitle || prevRoute.title || 'Back'} - - - - ); - }, - - RightButton(route) { - if (!route.rightButtonTitle) { - return null; - } - - return ( - - - - {route.rightButtonTitle} - - - - ); - }, - - Title(route) { - return ( - - - {route.title} - - - ); - }, -}; +export default SimpleApp; \ No newline at end of file diff --git a/examples/ReactExample/components/todo-itemsview.js b/examples/ReactExample/components/todo-itemsview.js new file mode 100644 index 00000000..404c1e0c --- /dev/null +++ b/examples/ReactExample/components/todo-itemsview.js @@ -0,0 +1,172 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +'use strict'; + +import React from 'react'; + +import { + Text, + View, +} from 'react-native'; + +import { ListView } from 'realm/react-native'; +import TodoItem from './todo-item'; +import realm from './realm'; +import styles from './styles'; + +export default class TodoItemsView extends React.Component { + constructor(props) { + super(props); + + let dataSource = new ListView.DataSource({ + rowHasChanged(a, b) { + // Always re-render TodoList items. + return a.done !== b.done || a.text !== b.text || a.items || b.items; + } + }); + + this.state = { + dataSource: this._cloneDataSource(dataSource, props), + }; + + this.renderRow = this.renderRow.bind(this); + } + + componentWillReceiveProps(props) { + this.updateDataSource(props); + } + + componentDidUpdate() { + let items = this.props.items; + let editingRow = this.state.editingRow; + + for (let i = items.length; i--;) { + if (i == editingRow) { + continue; + } + if (this._deleteItemIfEmpty(items[i]) && i < editingRow) { + editingRow--; + } + } + + if (editingRow != this.state.editingRow) { + this.setState({editingRow}); + } + } + + render() { + return ( + + + + Press Cmd+R to reload,{'\n'} + Cmd+D for dev menu + + + ); + } + + renderRow(item, sectionIndex, rowIndex) { + let RowClass; + let editing = false; + + // if (sectionIndex == 0) { + // RowClass = this.props.rowClass || TodoListItem; + // editing = this.state.editingRow == rowIndex; + // } else if (sectionIndex == 1) { + // RowClass = TodoListExtraItem; + // } + + return ( + this._onPressRow(item, sectionIndex, rowIndex)} + onPressDelete={() => this._onPressDeleteRow(item)} + onEndEditing={() => this._onEndEditingRow(item, rowIndex)} /> + ); + } + + updateDataSource(props=this.props) { + this.setState({ + dataSource: this._cloneDataSource(this.state.dataSource, props), + }); + } + + _cloneDataSource(dataSource, props) { + let items = props.items; + let extraItems = props.extraItems; + let sections = [items ? items.snapshot() : []]; + + if (extraItems && extraItems.length) { + sections.push(extraItems); + } + + return dataSource.cloneWithRowsAndSections(sections); + } + + _onPressRow(item, sectionIndex, rowIndex) { + let onPressItem = this.props.onPressItem; + if (onPressItem) { + onPressItem(item); + return; + } + + // If no handler was provided, then default to editing the row. + if (sectionIndex == 0) { + this.setState({editingRow: rowIndex}); + } + } + + _onPressDeleteRow(item) { + this._deleteItem(item); + this.updateDataSource(); + } + + _onEndEditingRow(item, rowIndex) { + if (this._deleteItemIfEmpty(item)) { + this.updateDataSource(); + } + if (this.state.editingRow == rowIndex) { + this.setState({editingRow: null}); + } + } + + _deleteItem(item) { + let items = item.items; + + realm.write(() => { + // If the item is a TodoList, then delete all of its items. + if (items && items.length) { + realm.delete(items); + } + + realm.delete(item); + }); + } + + _deleteItemIfEmpty(item) { + // The item could be a TodoList or a Todo. + if (!item.name && !item.text) { + this._deleteItem(item); + return true; + } + return false; + } +} diff --git a/examples/ReactExample/components/todo-list-item.js b/examples/ReactExample/components/todo-list-item.js index b82b823f..06137ac8 100644 --- a/examples/ReactExample/components/todo-list-item.js +++ b/examples/ReactExample/components/todo-list-item.js @@ -42,7 +42,12 @@ export default class TodoListItem extends React.Component { get done() { let items = this.props.item.items; - return items.length > 0 && items.every((item) => item.done); + if (items) { + return items.length > 0 && items.every((item) => item.done); + } + else { + return this.props.item.done; + } } get text() { diff --git a/examples/ReactExample/components/todo-listview.js b/examples/ReactExample/components/todo-listview.js index 4f94612a..f3904cc6 100644 --- a/examples/ReactExample/components/todo-listview.js +++ b/examples/ReactExample/components/todo-listview.js @@ -73,7 +73,7 @@ export default class TodoListView extends React.Component { render() { return ( - + Press Cmd+R to reload,{'\n'} Cmd+D for dev menu diff --git a/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj b/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj index ffc6720b..193e603d 100644 --- a/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj +++ b/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj @@ -5,7 +5,6 @@ }; objectVersion = 46; objects = { - /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; @@ -28,6 +27,7 @@ 8573DE511E23DDA700914396 /* ReactExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8573DE501E23DDA700914396 /* ReactExampleTests.m */; }; B06E5AD59A024665BD24C8C7 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C4594A7EF1647D68ADF0ED0 /* libz.tbd */; }; EF9CDEC26BA64438B1A9F856 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C202449017C94855B351AE73 /* libc++.tbd */; }; + F5A48833F58A45B2A8E9F550 /* libRNExitApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F25C98ECB41447FB9BA11E19 /* libRNExitApp.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -253,6 +253,8 @@ 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 8573DE501E23DDA700914396 /* ReactExampleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactExampleTests.m; sourceTree = ""; }; C202449017C94855B351AE73 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + A07E5AD22EFC47D594326945 /* RNExitApp.xcodeproj */ = {isa = PBXFileReference; name = "RNExitApp.xcodeproj"; path = "../node_modules/react-native-exit-app-no-history/ios/RNExitApp.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + F25C98ECB41447FB9BA11E19 /* libRNExitApp.a */ = {isa = PBXFileReference; name = "libRNExitApp.a"; path = "libRNExitApp.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -282,6 +284,7 @@ 146834051AC3E58100842450 /* libReact.a in Frameworks */, 70C063557D0D491D8F4D348F /* libRealmReact.a in Frameworks */, B06E5AD59A024665BD24C8C7 /* libz.tbd in Frameworks */, + F5A48833F58A45B2A8E9F550 /* libRNExitApp.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -447,6 +450,7 @@ 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, 73AD103601A44EB291AC2117 /* RealmReact.xcodeproj */, + A07E5AD22EFC47D594326945 /* RNExitApp.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -891,6 +895,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactExample.app/ReactExample"; @@ -908,6 +913,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactExample.app/ReactExample"; diff --git a/examples/ReactExample/package.json b/examples/ReactExample/package.json index 8fa0b93b..0c61f8ce 100644 --- a/examples/ReactExample/package.json +++ b/examples/ReactExample/package.json @@ -9,8 +9,8 @@ "dependencies": { "react": "16.0.0", "react-native": "0.50.4", - "react-native-deprecated-custom-components": "^0.1.1", - "react-navigation": "^1.0.0-beta.19", + "react-navigation": "^1.0.0-beta.21", + "react-native-exit-app-no-history": "^1.0.2", "realm": "file:../.." }, "devDependencies": { diff --git a/scripts/test.sh b/scripts/test.sh index 96c29ba0..df92e5fc 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -255,8 +255,11 @@ case "$TARGET" in open_chrome start_packager + echo "{ \"test\" : true }" > $(pwd)/components/params.json pushd ios xctest ReactExample + popd + echo "{}" > $(pwd)/components/params.json ;; "react-tests-android") npm run check-environment From 106cf09c278102ceb3f559ccc5d7408e78af84bf Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 13 Dec 2017 22:48:48 +0200 Subject: [PATCH 16/26] remove commented code --- .../ReactExample/components/items-screen.js | 43 +------------------ .../ReactExample/components/todo-itemsview.js | 7 --- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/examples/ReactExample/components/items-screen.js b/examples/ReactExample/components/items-screen.js index 02d2f095..5d7abfc9 100644 --- a/examples/ReactExample/components/items-screen.js +++ b/examples/ReactExample/components/items-screen.js @@ -54,51 +54,10 @@ export default class ItemsScreen extends React.Component { } render() { - // let objects = realm.objects('Todo'); - // let extraItems = [ - // { name: 'Complete', items: objects.filtered('done = true') }, - // { name: 'Incomplete', items: objects.filtered('done = false') }, - // ]; - + let properties = { - // ref: 'listView', - // extraItems: extraItems, - // onPressItem: this._onPressTodoList, } return ; } - - // renderScene(route) { - // console.log(this.todoLists); - // return - // } - - - - // _onPressTodoItem(list) { - // const { navigate } = this.props.navigation; - // let items = list.items; - - // let route = { - // title: list.name, - // component: TodoListView, - // passProps: { - // ref: 'listItemView', - // items: items, - // rowClass: TodoItem, - // }, - // }; - - // // Check if the items are mutable (i.e. List rather than Results). - // if (items.push) { - // Object.assign(route, { - // rightButtonTitle: 'Add', - // onRightButtonPress: () => this._addNewTodoItem(list), - // }); - // } - - // // this.refs.nav.push(route); - // navigate('TodoListItem', { items: items }) - // } } \ No newline at end of file diff --git a/examples/ReactExample/components/todo-itemsview.js b/examples/ReactExample/components/todo-itemsview.js index 404c1e0c..686e6107 100644 --- a/examples/ReactExample/components/todo-itemsview.js +++ b/examples/ReactExample/components/todo-itemsview.js @@ -86,13 +86,6 @@ export default class TodoItemsView extends React.Component { let RowClass; let editing = false; - // if (sectionIndex == 0) { - // RowClass = this.props.rowClass || TodoListItem; - // editing = this.state.editingRow == rowIndex; - // } else if (sectionIndex == 1) { - // RowClass = TodoListExtraItem; - // } - return ( Date: Thu, 14 Dec 2017 07:53:03 +0100 Subject: [PATCH 17/26] Add disableFormatUpgrade to Realm configuration (#1566) --- CHANGELOG.md | 1 + docs/realm.js | 3 +++ lib/index.d.ts | 1 + src/js_realm.hpp | 6 ++++++ tests/js/realm-tests.js | 8 ++++++++ 5 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1107f19c..9322fbdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ X.Y.Z Release notes ### Enhancements * Added property `Realm.isClosed` which indicates if a Realm instance is closed or not. +* Added property `disableFormatUpgrade` to the Realm configuration object which disables automatic file format upgrade when opening a Realm file. ### Bug fixes * None. diff --git a/docs/realm.js b/docs/realm.js index a6cf1a22..d4785253 100644 --- a/docs/realm.js +++ b/docs/realm.js @@ -307,6 +307,9 @@ Realm.defaultPath; * what fits in memory, but it is not persistent and will be removed when the last instance * is closed. * @property {boolean} [readOnly=false] - Specifies if this Realm should be opened as read-only. + * @property {boolean} [disableFormatUpgrade=false] - Specifies if this Realm's file format should + * be automatically upgraded if it was created with an older version of the Realm library. + * If set to `true` and a file format upgrade is required, an error will be thrown instead. * @property {Array} [schema] - Specifies all the * object types in this Realm. **Required** when first creating a Realm at this `path`. * If omitted, the schema will be read from the existing Realm file. diff --git a/lib/index.d.ts b/lib/index.d.ts index d7243ce9..b3529fb2 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -85,6 +85,7 @@ declare namespace Realm { schemaVersion?: number; sync?: Realm.Sync.SyncConfiguration; deleteRealmIfMigrationNeeded?: boolean; + disableFormatUpgrade?: boolean; } // object props type diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 2672721b..3bd59f3a 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -520,6 +520,12 @@ void RealmClass::constructor(ContextType ctx, ObjectType this_object, size_t if (!Value::is_undefined(ctx, cache_value)) { config.cache = Value::validated_to_boolean(ctx, cache_value, "_cache"); } + + static const String disable_format_upgrade_string = "disableFormatUpgrade"; + ValueType disable_format_upgrade_value = Object::get_property(ctx, object, disable_format_upgrade_string); + if (!Value::is_undefined(ctx, disable_format_upgrade_value)) { + config.disable_format_upgrade = Value::validated_to_boolean(ctx, disable_format_upgrade_value, "disableFormatUpgrade"); + } } } else { diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index 74538114..c0c22346 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -1181,4 +1181,12 @@ module.exports = { new Realm({schema: schema, deleteRealmIfMigrationNeeded: true, migration: function(oldRealm, newRealm) {}}); }, "Cannot include 'migration' when 'deleteRealmIfMigrationNeeded' is set.") }, + + testDisableFileFormatUpgrade: function() { + Realm.copyBundledRealmFiles(); + + TestCase.assertThrowsContaining(() => { + new Realm({ path: 'dates-v3.realm', disableFormatUpgrade: true } ); + }, 'The Realm file format must be allowed to be upgraded in order to proceed.'); + } }; From 97d695d61560699062e4c1c02a56278d136ebdbd Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 14 Dec 2017 12:17:49 +0200 Subject: [PATCH 18/26] fix android test app build and the script for android tests address review comments --- .gitignore | 1 + .../ReactExample/components/items-screen.js | 2 +- examples/ReactExample/components/styles.js | 2 -- examples/ReactExample/components/todo-app.js | 4 +--- scripts/react-tests-android.js | 2 +- scripts/test.sh | 23 +++++++++++++------ tests/react-test-app/android/app/build.gradle | 4 ++-- .../realm/react/testapp/MainApplication.java | 2 +- tests/react-test-app/android/settings.gradle | 1 + tests/react-test-app/index.android.js | 15 +++++++++--- tests/react-test-app/index.ios.js | 4 ++-- 11 files changed, 38 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 779d6a36..742ae0ae 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ packager_out.txt build.log.* realm-object-server/ tests/react-test-app/ios/out.txt +*.iml diff --git a/examples/ReactExample/components/items-screen.js b/examples/ReactExample/components/items-screen.js index 5d7abfc9..15bba8cb 100644 --- a/examples/ReactExample/components/items-screen.js +++ b/examples/ReactExample/components/items-screen.js @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////// // -// Copyright 2016 Realm Inc. +// Copyright 2017 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/ReactExample/components/styles.js b/examples/ReactExample/components/styles.js index 32f8f00b..25a9eecf 100644 --- a/examples/ReactExample/components/styles.js +++ b/examples/ReactExample/components/styles.js @@ -22,9 +22,7 @@ import { Platform, StyleSheet } from 'react-native'; -// import NavigationExperimental from 'react-native-deprecated-custom-components'; -// const { NavBarHeight, TotalNavHeight } = NavigationExperimental.Navigator.NavigationBar.Styles.General; const iOS = (Platform.OS == 'ios'); diff --git a/examples/ReactExample/components/todo-app.js b/examples/ReactExample/components/todo-app.js index cd8b4bd6..30cb5dd5 100644 --- a/examples/ReactExample/components/todo-app.js +++ b/examples/ReactExample/components/todo-app.js @@ -35,7 +35,6 @@ import ItemsScreen from './items-screen' import realm from './realm'; import styles from './styles'; -// import NavigationExperimental from 'react-native-deprecated-custom-components'; import { StackNavigator } from 'react-navigation'; import RNExitApp from 'react-native-exit-app-no-history'; @@ -59,7 +58,7 @@ class HomeScreen extends React.Component { this.todoLists.addListener((name, changes) => { console.log("changed: " + JSON.stringify(changes)); if (params) { - console.error("params.json indicate a test run. Exiting application"); + console.error("params.json indicates a test run. Exiting application"); RNExitApp.exitApp(); } }); @@ -67,7 +66,6 @@ class HomeScreen extends React.Component { // Bind all the methods that we will be passing as props. - // this.renderScene = this.renderScene.bind(this); this._addNewTodoList = this._addNewTodoList.bind(this); this._onPressTodoList = this._onPressTodoList.bind(this); diff --git a/scripts/react-tests-android.js b/scripts/react-tests-android.js index 798619d0..124d5e1e 100644 --- a/scripts/react-tests-android.js +++ b/scripts/react-tests-android.js @@ -64,6 +64,6 @@ shell.exec("adb shell am start -n io.realm.react.testapp/.MainActivity"); shell.popd(); -shell.exec("adb shell \"logcat -c && logcat | grep -m 1 __REALM_REACT_ANDROID_TESTS_COMPLETED__\""); +shell.exec("adb shell \"logcat -c && logcat | grep -m 1 __REALM_JS_TESTS_COMPLETED__\""); shell.exec("adb pull /sdcard/tests.xml"); \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh index df92e5fc..59c871b3 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -66,7 +66,7 @@ stop_server() { echo stopping server if [[ ${SERVER_PID} -gt 0 ]] ; then echo server is running. killing it - kill -9 ${SERVER_PID} || true + kill -9 ${SERVER_PID} >/dev/null 2>&1 || true fi } @@ -104,7 +104,7 @@ cleanup() { open_chrome() { if [ $CONFIGURATION == 'Release' ]; then - break + return; fi local dir @@ -152,6 +152,10 @@ xctest() { echo "Shuttting down ${SIM_DEVICE_NAME} simulator. (device is not deleted. you can use it to debug the app)" shutdown_ios_simulator + check_test_results $1 +} + +check_test_results() { echo "Checking tests results" if grep -q "REALM_FAILING_TESTS" $(pwd)/build/out.txt; then echo "*** REALM JS TESTS FAILED. See tests results above ***" @@ -167,7 +171,7 @@ setup_ios_simulator() { delete_ios_simulator >/dev/null 2>&1 #parse devices - IOS_RUNTIME=$(xcrun simctl list runtimes | grep -m1 -o '(com.apple.CoreSimulator.SimRuntime.iOS.*)' | sed 's/[()]//g') + IOS_RUNTIME=$(xcrun simctl list runtimes | grep -m1 -o 'com.apple.CoreSimulator.SimRuntime.iOS.*' | sed 's/[()]//g') echo using iOS Runtime ${IOS_RUNTIME} to create new simulator ${SIM_DEVICE_NAME} #create new test simulator @@ -271,21 +275,25 @@ case "$TARGET" in [[ $CONFIGURATION == 'Debug' ]] && exit 0 XCPRETTY='' - pushd tests/react-test-app + pushd react-native/android + $(pwd)/gradlew publishAndroid + popd + pushd tests/react-test-app npm install echo "Resetting logcat" # Despite the docs claiming -c to work, it doesn't, so `-T 1` alleviates that. + mkdir -p $(pwd)/build || true adb logcat -c - adb logcat -T 1 | tee "$LOGCAT_OUT" & + adb logcat -T 1 | tee "$LOGCAT_OUT" | tee $(pwd)/build/out.txt & ./run-android.sh echo "Start listening for Test completion" while :; do - if grep -q "__REALM_REACT_ANDROID_TESTS_COMPLETED__" "$LOGCAT_OUT"; then + if grep -q "__REALM_JS_TESTS_COMPLETED__" "$LOGCAT_OUT"; then break else echo "Waiting for tests." @@ -301,7 +309,8 @@ case "$TARGET" in echo "********* TESTS COMPLETED *********"; echo "********* File location: $(pwd)/tests.xml *********"; cat tests.xml - + + check_test_results ReactTests ;; "node") npm run check-environment diff --git a/tests/react-test-app/android/app/build.gradle b/tests/react-test-app/android/app/build.gradle index bec27402..02f202a0 100644 --- a/tests/react-test-app/android/app/build.gradle +++ b/tests/react-test-app/android/app/build.gradle @@ -137,13 +137,13 @@ android { } dependencies { - compile project(':react-native-exception-handler') - compile project(':react-native-exit-app-no-history') compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules compile project(":realm") compile project(":react-native-fs") + compile project(':react-native-exception-handler') + compile project(':react-native-exit-app-no-history') } // Run this once to be able to run the application with BUCK diff --git a/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java b/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java index 977b654f..5e7a2ca2 100644 --- a/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java +++ b/tests/react-test-app/android/app/src/main/java/io/realm/react/testapp/MainApplication.java @@ -22,7 +22,7 @@ public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override - protected boolean getUseDeveloperSupport() { + public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } diff --git a/tests/react-test-app/android/settings.gradle b/tests/react-test-app/android/settings.gradle index 98eee976..9c5c13ed 100644 --- a/tests/react-test-app/android/settings.gradle +++ b/tests/react-test-app/android/settings.gradle @@ -10,5 +10,6 @@ project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/ include ':react-native-exception-handler' project(':react-native-exception-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exception-handler/android') + include ':react-native-exit-app-no-history' project(':react-native-exit-app-no-history').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exit-app-no-history/android') diff --git a/tests/react-test-app/index.android.js b/tests/react-test-app/index.android.js index 6e01d26b..3380a2f0 100644 --- a/tests/react-test-app/index.android.js +++ b/tests/react-test-app/index.android.js @@ -33,13 +33,13 @@ import { getTestNames, runTest } from './tests'; async function runTests() { let testNames = getTestNames(); let rootXml = builder.create('testsuites'); - + let failingTests = []; for (let suiteName in testNames) { let itemTestsuite = rootXml.ele('testsuite'); let nbrTests = 0; let nbrFailures = 0; - console.log('Starting ' + suiteName); + console.error('Starting ' + suiteName); for (let testName of testNames[suiteName]) { nbrTests++; @@ -51,6 +51,7 @@ async function runTests() { await runTest(suiteName, testName); } catch (e) { + failingTests.push(`${suiteName}: ${testName} : Error ${e.message}`); itemTest.ele('error', {'message': e.message, 'stacktrace': e.stack}, e.toString()); nbrFailures++; } @@ -73,11 +74,19 @@ async function runTests() { // write the unit tests reports try { await RNFS.writeFile('/sdcard/tests.xml', xmlString, 'utf8'); - console.log('__REALM_REACT_ANDROID_TESTS_COMPLETED__'); + console.log('__REALM_JS_TESTS_COMPLETED__'); + if (failingTests.length !== 0) { + console.error('\n\nREALM_FAILING_TESTS\n'); + console.error(failingTests); + } } catch (e) { console.error(e); } + finally { + console.warn("Realm Tests App finished. Exiting. Disable this to debug the app locally"); + RNExitApp.exitApp(); + } } class ReactTests extends React.Component { diff --git a/tests/react-test-app/index.ios.js b/tests/react-test-app/index.ios.js index f12ced2b..b7bc0e70 100644 --- a/tests/react-test-app/index.ios.js +++ b/tests/react-test-app/index.ios.js @@ -99,7 +99,7 @@ async function runTests() { //using console.log output is not shown in Release builds. using console.warn console.warn(xmlString); - console.warn('__REALM_REACT_IOS_TESTS_COMPLETED__'); + console.warn('__REALM_JS_TESTS_COMPLETED__'); if (failingTests.length !== 0) { console.error('\n\nREALM_FAILING_TESTS\n'); console.error(failingTests); @@ -109,7 +109,7 @@ async function runTests() { console.error(e); } finally { - console.warn("Realm iOS Tests App finished. Exiting. Disable this to debug the app locally"); + console.warn("Realm Tests App finished. Exiting. Disable this to debug the app locally"); RNExitApp.exitApp(); } } From d84e1f8c65468c2114c469c41854bf615c6077fe Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Thu, 14 Dec 2017 13:36:18 +0100 Subject: [PATCH 19/26] [2.1.0] Bump version --- CHANGELOG.md | 5 ++--- dependencies.list | 2 +- package.json | 2 +- src/RealmJS.xcodeproj/project.pbxproj | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9322fbdb..e7184724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,4 @@ - -X.Y.Z Release notes +2.1.0 Release notes (2017-12-14) ============================================================= ### Breaking changes * None. @@ -12,7 +11,7 @@ X.Y.Z Release notes * None. ### Internal -* None. +* Updated to React Native 0.50.4 (test and example apps). 2.0.13 Release notes (2017-12-8) ============================================================= diff --git a/dependencies.list b/dependencies.list index 3e987eb2..3403b27f 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js -VERSION=2.0.13 +VERSION=2.1.0 REALM_CORE_VERSION=4.0.2 REALM_SYNC_VERSION=2.1.8 REALM_OBJECT_SERVER_VERSION=2.0.21 diff --git a/package.json b/package.json index 19f0a8c0..90f55d65 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "realm", "description": "Realm is a mobile database: an alternative to SQLite and key-value stores", - "version": "2.0.13", + "version": "2.1.0", "license": "Apache-2.0", "homepage": "https://realm.io", "keywords": [ diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index 09540f69..67c32298 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -998,7 +998,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2.0.13; + CURRENT_PROJECT_VERSION = 2.1.0; CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1062,7 +1062,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2.0.13; + CURRENT_PROJECT_VERSION = 2.1.0; CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; From 5ed9eba0a2f6a6b88ac8968b99654601bf8d783f Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Fri, 15 Dec 2017 17:25:42 +0100 Subject: [PATCH 20/26] Updating to Realm Sync 2.1.10 (#1573) --- CHANGELOG.md | 15 +++++++++++++++ dependencies.list | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7184724..fd29af54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +x.y.z Release notes +============================================================= +### Breaking changes +* None. + +### Enhancements +* None. + +### Bug fixes +* [Object Server] Fixed a bug where long reconnection happens when a proxy in front of the sync worker returns one of those. + +### Internal +* Updated to Realm Sync 2.1.10 (see "Bug fixes"). + + 2.1.0 Release notes (2017-12-14) ============================================================= ### Breaking changes diff --git a/dependencies.list b/dependencies.list index 3403b27f..bef5a39e 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js VERSION=2.1.0 REALM_CORE_VERSION=4.0.2 -REALM_SYNC_VERSION=2.1.8 +REALM_SYNC_VERSION=2.1.10 REALM_OBJECT_SERVER_VERSION=2.0.21 From bf7ac66d97d7bc974c65f7d5069b1697ed838723 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Fri, 15 Dec 2017 17:33:45 +0100 Subject: [PATCH 21/26] [2.1.1] Bump version --- CHANGELOG.md | 2 +- dependencies.list | 2 +- package.json | 2 +- src/RealmJS.xcodeproj/project.pbxproj | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd29af54..2f4c6e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -x.y.z Release notes +2.1.1 Release notes (2017-12-15) ============================================================= ### Breaking changes * None. diff --git a/dependencies.list b/dependencies.list index bef5a39e..948574b0 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js -VERSION=2.1.0 +VERSION=2.1.1 REALM_CORE_VERSION=4.0.2 REALM_SYNC_VERSION=2.1.10 REALM_OBJECT_SERVER_VERSION=2.0.21 diff --git a/package.json b/package.json index 90f55d65..6f51aca8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "realm", "description": "Realm is a mobile database: an alternative to SQLite and key-value stores", - "version": "2.1.0", + "version": "2.1.1", "license": "Apache-2.0", "homepage": "https://realm.io", "keywords": [ diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index 67c32298..d55c57aa 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -998,7 +998,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2.1.0; + CURRENT_PROJECT_VERSION = 2.1.1; CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1062,7 +1062,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2.1.0; + CURRENT_PROJECT_VERSION = 2.1.1; CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; From 9505b750f9b8321fb9bb84aeb9d902357aecd069 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Fri, 15 Dec 2017 18:57:57 +0100 Subject: [PATCH 22/26] Updating object store (#1575) --- src/object-store | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object-store b/src/object-store index 3eb19c01..d681b1fb 160000 --- a/src/object-store +++ b/src/object-store @@ -1 +1 @@ -Subproject commit 3eb19c014fdfa0f02a03d4acf71d046d29a6dfa6 +Subproject commit d681b1fb8a8ca7a8db1ab1edc25771d984795ebe From c2de29ab0cde82b56d6d620071c0a6c916db0348 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 18 Dec 2017 14:46:26 -0800 Subject: [PATCH 23/26] Fix an issue where `Realm.open` would complain about the Realm already being open with a different schema version In order to correctly open read-only synchronized Realms, `Realm.open` would open the Realm without specifying a schema or schema version, wait for any remote changes to be downloaded (if appropriate), and then re-open the Realm with the specified schema and schema version. This would lead to an exception about the Realm being open with a different schema version if the Realm had previously been opened with a different schema version, due to the way `RealmCoordinator` caches information about the schema of open Realms. We address this by making two changes: 1. `Realm.open` for non-synchronized Realms no longer goes through `_waitForDownload`. This means the dance described above where the Realm is opened twice is not used for local Realms. 2. `_waitForDownload` no longer keeps the `Realm` alive until after its callback has returned. It instead keeps the `SyncSession` alive. This is sufficient to avoid the connection being torn down and having to reconnect when `_waitForDownload`'s callback later opens the Realm with the correct schema and schema version, while also allowing for `RealmCoordinator`'s cached information to be cleared when the schemaless Realm is closed prior to the Realm being reopened. In addition, tests have been added that reproduced the problem in both a local and sync context. --- CHANGELOG.md | 1 + lib/extensions.js | 21 +++++-- src/js_realm.hpp | 113 +++++++++++++++++++------------------- tests/js/realm-tests.js | 15 +++++ tests/js/session-tests.js | 44 +++++++++++++++ 5 files changed, 131 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f4c6e77..acdd4c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bug fixes * [Object Server] Fixed a bug where long reconnection happens when a proxy in front of the sync worker returns one of those. +* Fix a bug where `Realm.open` could unexpectedly raise an "Realm at path ... already opened with different schema version" error. ### Internal * Updated to Realm Sync 2.1.10 (see "Bug fixes"). diff --git a/lib/extensions.js b/lib/extensions.js index 467e3347..f1c04695 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -62,16 +62,28 @@ module.exports = function(realmConstructor) { //Add async open API Object.defineProperties(realmConstructor, getOwnPropertyDescriptors({ open(config) { + // For local Realms we open the Realm and return it in a resolved Promise. + if (!("sync" in config)) { + let promise = Promise.resolve(new realmConstructor(config)); + promise.progress = (callback) => { }; + return promise; + } + + // For synced Realms we open the Realm without specifying the schema and then wait until + // the Realm has finished its initial sync with the server. We then reopen it with the correct + // schema. This avoids writing the schema to a potentially read-only Realm file, which would + // result in sync rejecting the writes. `_waitForDownload` ensures that the session is kept + // alive until our callback has returned, which prevents it from being torn down and recreated + // when we close the schemaless Realm and open it with the correct schema. let syncSession; let promise = new Promise((resolve, reject) => { let realm = new realmConstructor(waitForDownloadConfig(config)); realm._waitForDownload( - (session) => { - syncSession = session; - }, + (session) => { syncSession = session; }, (error) => { + realm.close(); if (error) { - setTimeout(() => { reject(error); }, 1); + setTimeout(() => { reject(error); }, 1); } else { try { @@ -91,7 +103,6 @@ module.exports = function(realmConstructor) { return promise; }; - return promise; }, diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 3bd59f3a..5a434e64 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -245,9 +245,9 @@ public: {"close", wrap}, {"compact", wrap}, {"deleteModel", wrap}, - {"_waitForDownload", wrap}, {"_objectForObjectId", wrap}, #if REALM_ENABLE_SYNC + {"_waitForDownload", wrap}, {"_subscribeToObjects", wrap}, #endif }; @@ -720,6 +720,7 @@ void RealmClass::get_sync_session(ContextType ctx, ObjectType object, ReturnV } #endif +#if REALM_ENABLE_SYNC template void RealmClass::wait_for_download_completion(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) { args.validate_maximum(2); @@ -730,67 +731,63 @@ void RealmClass::wait_for_download_completion(ContextType ctx, ObjectType thi session_callback = Value::validated_to_function(ctx, args[0]); } -#if REALM_ENABLE_SYNC auto realm = *get_internal>(this_object); - if (auto* sync_config = realm->config().sync_config.get()) { - Protected protected_callback(ctx, callback_function); - Protected protected_this(ctx, this_object); - Protected protected_ctx(Context::get_global_context(ctx)); - - EventLoopDispatcher wait_handler([=](std::error_code error_code) { - HANDLESCOPE - if (!error_code) { - //success - Function::callback(protected_ctx, protected_callback, typename T::Object(), 0, nullptr); - } - else { - //fail - ObjectType object = Object::create_empty(protected_ctx); - Object::set_property(protected_ctx, object, "message", Value::from_string(protected_ctx, error_code.message())); - Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, error_code.value())); - - ValueType callback_arguments[1]; - callback_arguments[0] = object; - - Function::callback(protected_ctx, protected_callback, typename T::Object(), 1, callback_arguments); - } - - // We keep our Realm instance alive until the callback has had a chance to open its own instance. - // This allows it to share the sync session that our Realm opened. - if (realm) - realm->close(); - }); - - std::shared_ptr user = sync_config->user; - if (user && user->state() != SyncUser::State::Error) { - if (auto session = user->session_for_on_disk_path(realm->config().path)) { - if (!Value::is_null(ctx, session_callback)) { - FunctionType session_callback_func = Value::to_function(ctx, session_callback); - auto syncSession = create_object>(ctx, new WeakSession(session)); - ValueType callback_arguments[1]; - callback_arguments[0] = syncSession; - Function::callback(protected_ctx, session_callback_func, typename T::Object(), 1, callback_arguments); - } - - session->wait_for_download_completion(std::move(wait_handler)); - return; - } - } - - ObjectType object = Object::create_empty(protected_ctx); - Object::set_property(protected_ctx, object, "message", - Value::from_string(protected_ctx, "Cannot asynchronously open synced Realm because the associated session previously experienced a fatal error")); - Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, 1)); - - ValueType callback_arguments[1]; - callback_arguments[0] = object; - Function::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments); - return; + auto* sync_config = realm->config().sync_config.get(); + if (!sync_config) { + throw std::logic_error("_waitForDownload can only be used on a synchronized Realm."); } -#endif - Function::callback(ctx, callback_function, this_object, 0, nullptr); + Protected protected_callback(ctx, callback_function); + Protected protected_this(ctx, this_object); + Protected protected_ctx(Context::get_global_context(ctx)); + + std::shared_ptr user = sync_config->user; + if (user && user->state() != SyncUser::State::Error) { + if (auto session = user->session_for_on_disk_path(realm->config().path)) { + if (!Value::is_null(ctx, session_callback)) { + FunctionType session_callback_func = Value::to_function(ctx, session_callback); + auto syncSession = create_object>(ctx, new WeakSession(session)); + ValueType callback_arguments[1]; + callback_arguments[0] = syncSession; + Function::callback(protected_ctx, session_callback_func, typename T::Object(), 1, callback_arguments); + } + + EventLoopDispatcher wait_handler([=](std::error_code error_code) { + HANDLESCOPE + if (!error_code) { + //success + Function::callback(protected_ctx, protected_callback, typename T::Object(), 0, nullptr); + } + else { + //fail + ObjectType object = Object::create_empty(protected_ctx); + Object::set_property(protected_ctx, object, "message", Value::from_string(protected_ctx, error_code.message())); + Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, error_code.value())); + + ValueType callback_arguments[1]; + callback_arguments[0] = object; + + Function::callback(protected_ctx, protected_callback, typename T::Object(), 1, callback_arguments); + } + // Ensure that the session remains alive until the callback has had an opportunity to reopen the Realm + // with the appropriate schema. + (void)session; + }); + session->wait_for_download_completion(std::move(wait_handler)); + return; + } + } + + ObjectType object = Object::create_empty(protected_ctx); + Object::set_property(protected_ctx, object, "message", + Value::from_string(protected_ctx, "Cannot asynchronously open synced Realm because the associated session previously experienced a fatal error")); + Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, 1)); + + ValueType callback_arguments[1]; + callback_arguments[0] = object; + Function::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments); } +#endif template void RealmClass::objects(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) { diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index c0c22346..61a07b66 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -207,6 +207,21 @@ module.exports = { TestCase.assertEqual(realm.readOnly, true); }, + testRealmOpen: function() { + let realm = new Realm({schema: [schemas.TestObject], schemaVersion: 1}); + realm.write(() => { + realm.create('TestObject', [1]) + }); + realm.close(); + + return Realm.open({schema: [schemas.TestObject], schemaVersion: 2}).then(realm => { + const objects = realm.objects('TestObject'); + TestCase.assertEqual(objects.length, 1); + TestCase.assertEqual(objects[0].doubleCol, 1.0); + realm.close(); + }); + }, + testDefaultPath: function() { const defaultPath = Realm.defaultPath; let defaultRealm = new Realm({schema: []}); diff --git a/tests/js/session-tests.js b/tests/js/session-tests.js index c7859074..25232d84 100644 --- a/tests/js/session-tests.js +++ b/tests/js/session-tests.js @@ -169,6 +169,50 @@ module.exports = { }); }, + testRealmOpenWithExistingLocalRealm() { + if (!isNodeProccess) { + return; + } + + const username = uuid(); + const realmName = uuid(); + const expectedObjectsCount = 3; + + let user, config; + return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) + .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(u => { + user = u; + const accessTokenRefreshed = this; + let successCounter = 0; + + config = { + sync: { user, url: `realm://localhost:9080/~/${realmName}` }, + schema: [{ name: 'Dog', properties: { name: 'string' } }], + schemaVersion: 1, + }; + + // Open the Realm with a schema version of 1, then immediately close it. + // This verifies that Realm.open doesn't hit issues when the schema version + // of an existing, local Realm is different than the one passed in the configuration. + let realm = new Realm(config); + realm.close(); + + config.schemaVersion = 2; + return Realm.open(config) + }).then(realm => { + let actualObjectsCount = realm.objects('Dog').length; + TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count"); + + const session = realm.syncSession; + TestCase.assertInstanceOf(session, Realm.Sync.Session); + TestCase.assertEqual(session.user.identity, user.identity); + TestCase.assertEqual(session.config.url, config.sync.url); + TestCase.assertEqual(session.config.user.identity, config.sync.user.identity); + TestCase.assertEqual(session.state, 'active'); + }); + }, + testRealmOpenAsync() { if (!isNodeProccess) { return; From a4e2f31138bbe836b819ef05d05c2ce8291157e3 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 18 Dec 2017 18:04:50 -0800 Subject: [PATCH 24/26] Fix a typo in the change log --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acdd4c22..7a985ad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ### Bug fixes * [Object Server] Fixed a bug where long reconnection happens when a proxy in front of the sync worker returns one of those. -* Fix a bug where `Realm.open` could unexpectedly raise an "Realm at path ... already opened with different schema version" error. +* Fix a bug where `Realm.open` could unexpectedly raise a "Realm at path ... already opened with different schema version" error. ### Internal * Updated to Realm Sync 2.1.10 (see "Bug fixes"). From db8c166bb522dfc418f6b1feaa7ca694e8fdb608 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Dec 2017 11:17:12 +0200 Subject: [PATCH 25/26] Update CHANGELOG.md --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a985ad9..bec9119a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +X.Y.Z Release notes +============================================================= +### Breaking changes +* None. + +### Enhancements +* None. + +### Bug fixes +* Fix a bug where `Realm.open` could unexpectedly raise a "Realm at path ... already opened with different schema version" error. + +### Internal +* None + 2.1.1 Release notes (2017-12-15) ============================================================= ### Breaking changes From 839d8563a17fdf45ece9821a766d2ffa9fd9f940 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Wed, 20 Dec 2017 14:19:42 +0100 Subject: [PATCH 26/26] Adding test of timestamp's ms precision. (#1561) * Adding test of timestamp's ms precision. --- tests/js/object-tests.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/js/object-tests.js b/tests/js/object-tests.js index 88792067..6b8fa25d 100644 --- a/tests/js/object-tests.js +++ b/tests/js/object-tests.js @@ -453,5 +453,25 @@ module.exports = { TestCase.assertEqual(realm.objects('Date')[2].currentDate.getTime(), 1000000000000); TestCase.assertEqual(realm.objects('Date')[3].currentDate.getTime(), -1000000000000); TestCase.assertEqual(realm.objects('Date')[4].currentDate.toString(), stringifiedDate.toString()); + }, + + testDateResolution: function() { + const dateObjectSchema = { + name: 'DateObject', + properties: { + dateCol: 'date' + } + } + + var realm = new Realm({schema: [dateObjectSchema]}) + realm.write(function() { + realm.create('DateObject', { dateCol: new Date('2017-12-07T20:16:03.837Z') }) + }) + + var objects = realm.objects('DateObject') + TestCase.assertEqual(new Date('2017-12-07T20:16:03.837Z').getTime(), objects[0].dateCol.getTime()) + TestCase.assertTrue(new Date('2017-12-07T20:16:03.837Z').toISOString() === objects[0].dateCol.toISOString()) + + realm.close() } };