Node.js Win32 support (#864)

* Remove the windows install check

* Node.js Win32 support

* Bring back the `REALM_HAVE_CONFIG` definition

* Download core for Windows when building

* Implement cross-platform node platform.cpp with libuv

* wip

* Make jasmine run quicker

https://github.com/jasmine/jasmine/issues/1204

* Wait for worker process to close in AsyncTests

* Cross-platform paths in tests

* Normalize path separator for forward slash on Windows

* MSVC exception voodoo

* cross-platform uv_cwd

* fix linux build

* make the prepublish script cross-platform

* Disable encryption tests on windows

* ignore vendor/realm-node

* jenkinsfile work

* Only run the prepublish script for publish and pack

* Jenkinsfile work

* Include gyp files in package

* rewrite default_realm_file_directory()

* fix React Native Android build

* delete all realm artifacts in remove_realm_files_from_directory

* bring back build environment variables

* node-pre-gyp windows

* Update CHANGELOG.md
This commit is contained in:
Yavor Georgiev 2017-03-07 23:24:30 +01:00 committed by GitHub
parent b93cfaf909
commit db8ebd9333
28 changed files with 517 additions and 215 deletions

1
.gitignore vendored
View File

@ -22,6 +22,7 @@ sync
sync-*
node-sync
node-sync-*
vendor/realm-node*
# sh build.sh config
/Realm/config.mk

View File

@ -1,3 +1,14 @@
X.Y.Z Release notes
=============================================================
### Breaking changes
* None
### Enhancements
* Add support for Node.js on Windows
### Bug fixes
* None
1.0.3 Release notes
=============================================================
### Breaking changes

21
Jenkinsfile vendored
View File

@ -74,7 +74,8 @@ stage('build') {
macos_react_example_release: doMacBuild('react-example Release'),
//android_react_tests: doAndroidBuild('react-tests-android', {
// junit 'tests/react-test-app/tests.xml'
//})
//}),
windows_node: doWindowsBuild()
)
}
@ -201,3 +202,21 @@ def doReactBuild(target, postStep = null) {
}
}
def doWindowsBuild() {
return {
node('windows') {
unstash 'source'
try {
bat 'npm install --build-from-source'
dir('tests') {
bat 'npm install'
bat 'npm run test-nosync'
junit 'junitresults-*.xml'
}
} finally {
deleteDir()
}
}
}
}

View File

@ -4,8 +4,8 @@
"realm_download_binaries%": "1"
},
"includes": [
"src/node/gyp/target_defaults.gypi",
"src/node/gyp/realm.gyp"
"target_defaults.gypi",
"realm.gyp"
],
"targets": [
{
@ -66,6 +66,11 @@
"src/object-store/src/util/format.cpp",
],
"conditions": [
["OS=='win'", {
"sources": [
"src/object-store/src/impl/windows/external_commit_helper.cpp",
]
}],
["OS=='linux'", {
"sources": [
"src/object-store/src/impl/epoll/external_commit_helper.cpp",

View File

@ -39,6 +39,8 @@
"tests",
"vendor",
"binding.gyp",
"realm.gyp",
"target_defaults.gypi",
"dependencies.list"
],
"scripts": {
@ -49,10 +51,12 @@
"jsdoc": "rm -rf docs/output && jsdoc -c docs/conf.json",
"lint": "eslint",
"test": "scripts/test.sh",
"install": "node scripts/install.js",
"prepublish": "scripts/prepublish.sh"
"install": "node-pre-gyp install --fallback-to-build",
"prepublish": "node scripts/prepublish.js"
},
"dependencies": {
"extract-zip": "^1.6.0",
"ini": "^1.3.4",
"nan": "^2.3.3",
"node-fetch": "^1.6.3",
"node-pre-gyp": "^0.6.30",

143
realm.gyp Normal file
View File

@ -0,0 +1,143 @@
{
"variables": {
"use_realm_debug%": "<!(node -p \"'REALMJS_USE_DEBUG_CORE' in process.env ? 1 : 0\")"
},
"conditions": [
["OS=='mac'", {
"variables": {
"realm_enable_sync%": "1"
}
}, {
"variables": {
"realm_enable_sync%": "0"
}
}],
["OS=='win'", {
"conditions": [
["target_arch == 'ia32'", {
"variables": {
"realm_library_suffix": "-x86"
}
}, {
"variables": {
"realm_library_suffix": "-<(target_arch)"
}
}]
]
}, {
"variables": {
"realm_library_suffix": "-node"
}
}]
],
"targets": [
{
"target_name": "realm-core",
"type": "none",
"direct_dependent_settings": {
"conditions": [
["use_realm_debug", {
"defines": [ "REALM_DEBUG=1" ],
"libraries": [ "-lrealm<(realm_library_suffix)-dbg" ]
}, {
"libraries": [ "-lrealm<(realm_library_suffix)" ]
}]
]
},
"all_dependent_settings": {
"defines": [ "REALM_PLATFORM_NODE=1", "REALM_ENABLE_SYNC=<(realm_enable_sync)" ]
},
"variables": {
"prefix": "<!(node -p \"process.env.REALM_CORE_PREFIX || String()\")"
},
"conditions": [
["prefix!=''", {
"all_dependent_settings": {
"include_dirs": [ "<(prefix)/src" ],
},
"direct_dependent_settings": {
"library_dirs": [ "<(prefix)/src/realm" ]
}
}, {
"dependencies": [ "vendored-realm" ]
}],
["OS=='win'", {
"all_dependent_settings": {
"defines": [ "PTW32_STATIC_LIB" ]
}
}, {
"all_dependent_settings": {
"defines": [ "REALM_HAVE_CONFIG" ]
}
}]
]
},
{
"target_name": "realm-sync",
"type": "none",
"dependencies": [ "realm-core" ], # sync headers include core headers
"direct_dependent_settings": {
"conditions": [
["use_realm_debug", {
"libraries": [ "-lrealm-sync<(realm_library_suffix)-dbg" ]
}, {
"libraries": [ "-lrealm-sync<(realm_library_suffix)" ]
}]
]
},
"export_dependent_settings": [ "realm-core" ], # depending on sync is tantamount to depending on core
"variables": {
"prefix": "<!(node -p \"process.env.REALM_SYNC_PREFIX || String()\")"
},
"conditions": [
["prefix!=''", {
"all_dependent_settings": {
"include_dirs": [ "<(prefix)/src" ],
},
"direct_dependent_settings": {
"library_dirs": [ "<(prefix)/src/realm" ]
}
}, {
"dependencies": [ "vendored-realm" ]
}]
],
},
{
"variables": {
"vendor_dir%": "<(module_root_dir)/vendor"
},
"target_name": "vendored-realm",
"type": "none",
"all_dependent_settings": {
"include_dirs": [ "<(module_root_dir)/vendor/realm-node/include" ],
"library_dirs": [
"<(module_root_dir)/vendor/realm-node/",
"<(module_root_dir)/vendor/realm-node/lib",
"<(module_root_dir)/vendor/realm-node/osx"
]
},
"conditions": [
["realm_download_binaries and OS=='win'", {
"actions": [
{
"action_name": "download-realm",
"inputs": [ "<(module_root_dir)/scripts/download-realm.js" ],
"outputs": [ "<(module_root_dir)/vendor/realm-node" ],
"action": [ "node", "<(module_root_dir)/scripts/download-realm.js", "node", "<(use_realm_debug)" ]
}
]
}],
["realm_download_binaries and OS!='win'", {
"actions": [
{
"action_name": "download-realm",
"inputs": [ ],
"outputs": [ "<(module_root_dir)/vendor/realm-node" ],
"action": [ "<(module_root_dir)/scripts/download-core.sh", "node", "<(realm_enable_sync)" ]
}
]
}]
]
}
]
}

View File

@ -0,0 +1,29 @@
<#
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.
#>
Push-Location $PSScriptRoot\..
New-Item .\out -ItemType "directory"
npm install --ignore-scripts
foreach ($arch in "ia32", "x64") {
foreach ($version in "4.0.0", "5.0.0", "6.0.0", "7.0.0") {
Remove-Item .\build, .\compiled -Recurse -Force -ErrorAction Ignore
.\node_modules\node-pre-gyp\bin\node-pre-gyp.cmd rebuild --target_arch=$arch --target=$version
.\node_modules\node-pre-gyp\bin\node-pre-gyp.cmd package --target_arch=$arch --target=$version
Copy-Item .\build\stage\node-pre-gyp\*.tar.gz -Destination .\out
}
}

View File

@ -20,8 +20,8 @@ fi
# The 'node' argument will result in realm-node build being downloaded.
if [ "$1" = 'node' ]; then
ENABLE_SYNC="$2"
CORE_DIR="core-node"
SYNC_DIR='node-sync'
CORE_DIR="realm-node"
SYNC_DIR='realm-node'
if [ "$(uname)" = 'Darwin' ]; then
PLATFORM_TAG="node-osx-"

67
scripts/download-realm.js Normal file
View File

@ -0,0 +1,67 @@
////////////////////////////////////////////////////////////////////////////
//
// 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';
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const ini = require('ini').parse;
const unzip = require('extract-zip');
function download(url, destination) {
return fetch(url).then((response) => {
if (response.status !== 200) {
throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
} else if (response.headers.get('content-type') !== 'application/zip') {
throw new Error(`Unexpected response content type - ${response.headers.get('content-type')}`);
} else {
return new Promise((resolve) => {
const file = fs.createWriteStream(destination);
response.body.pipe(file)
.on('finish', () => {
file.close(resolve);
});
});
}
});
}
function extract(archive, destination) {
return new Promise((resolve, reject) => {
unzip(archive, { dir: destination }, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
const dependencies = ini(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));
const coreArchive = `realm-core-windows-${dependencies.REALM_CORE_VERSION}.zip`;
const coreUrl = `https://static.realm.io/downloads/core/${coreArchive}`;
const vendorDir = path.resolve(__dirname, '../vendor');
const downloadedCoreArchive = path.resolve(vendorDir, coreArchive);
const realmDir = path.resolve(vendorDir, 'realm-node');
if (!fs.existsSync(realmDir)) {
const downloadTask = fs.existsSync(downloadedCoreArchive) ? Promise.resolve() : download(coreUrl, downloadedCoreArchive);
downloadTask.then(() => extract(downloadedCoreArchive, realmDir));
}

View File

@ -1,29 +0,0 @@
/* eslint-disable no-console */
'use strict';
var spawn = require('child_process').spawn;
var isReactNative = false;
try {
require.resolve('react-native');
// If the above opeation didn't throw, this project has react native as a dependency.
isReactNative = true;
} catch(e) {}
// Not a React Native install. Must be either Node or Electron.
var isNode = !isReactNative;
if (process.platform === 'win32') {
if (isNode) {
console.error('ERROR: Realm is not yet supported for Node on Windows');
process.exit(-1);
} else {
console.warn('NOTE: Realm is not supported for Node on Windows, so you will not be able to run unit tests that rely on Realm with a node-based runner.');
}
} else {
// Execute "node-pre-gyp install --fallback-to-build
var pregyp = spawn('node-pre-gyp', ['install', '--fallback-to-build']);
pregyp.stdout.on('data', function (data) { console.log(data.toString()); });
pregyp.stderr.on('data', function (data) { console.error(data.toString()); });
pregyp.on('exit', function (code) { process.exit(code); });
}

44
scripts/prepublish.js Normal file
View File

@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////////////////////
//
// 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';
const fs = require('fs');
const path = require('path');
const exec = require('child_process').execFileSync;
const dependencies = ini(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));
console.log(`Core version: ${dependencies.REALM_CORE_VERSION}`);
console.log(`Sync version: ${dependencies.REALM_SYNC_VERSION}`);
if ('REALM_BUILD_ANDROID' in process.env) {
const gradlew = process.platform === 'win32' ? 'gradlew.bat' : 'gradlew';
const androidPath = path.resolve(__dirname, '../react-native/android');
exec(`${androidPath}/${gradlew}`, ['publishAndroid', '-PbuildWithSync=true'], { cwd: androidPath, stdio: 'inherit' });
}
function ini(string) {
const result = Object.create(null);
for (const line of string.split(/\r?\n/)) {
const parts = line.split('=');
result[parts[0]] = parts[1];
}
return result;
}

View File

@ -1,11 +0,0 @@
#!/bin/bash
set -e
set -o pipefail
export REALM_CORE_VERSION=$(./scripts/download-core.sh --version)
echo "Core Version: $REALM_CORE_VERSION"
cd "$(dirname "$0")/.."
if [ -n "$REALM_BUILD_ANDROID" ]; then
(cd react-native/android && ./gradlew publishAndroid -PbuildWithSync=true)
fi

View File

@ -35,7 +35,7 @@ namespace js {
template<typename T>
class List : public realm::List {
public:
List(std::shared_ptr<Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {}
List(std::shared_ptr<realm::Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {}
List(const realm::List &l) : realm::List(l) {}
std::vector<std::pair<Protected<typename T::Function>, NotificationToken>> m_notification_tokens;

View File

@ -30,8 +30,14 @@ namespace js {
static std::string s_default_path = "";
std::string default_path() {
if (s_default_path.size() == 0) {
s_default_path = realm::default_realm_file_directory() + "/default.realm";
if (s_default_path.empty()) {
s_default_path = realm::default_realm_file_directory() +
#if defined(WIN32) && WIN32
'\\'
#else
'/'
#endif
+ "default.realm";
}
return s_default_path;
}

View File

@ -262,9 +262,16 @@ public:
}
static std::string normalize_path(std::string path) {
if (path.size() && path[0] != '/' && path[0] != '.') {
return default_realm_file_directory() + "/" + path;
#if defined(WIN32) && WIN32
if (path.size() > 1 && path[0] != '\\' && path[1] != ':') {
path = default_realm_file_directory() + "\\" + path;
}
std::replace(path.begin(), path.end(), '/', '\\');
#else
if (path.size() && path[0] != '/' && path[0] != '.') {
path = default_realm_file_directory() + "/" + path;
}
#endif
return path;
}
};
@ -428,7 +435,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
template<typename T>
SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Config config, bool schema_updated,
ObjectDefaultsMap && defaults, ConstructorMap && constructors) {
config.execution_context = reinterpret_cast<AbstractExecutionContextID>(Context<T>::get_execution_context_id(ctx));
config.execution_context = Context<T>::get_execution_context_id(ctx);
SharedRealm realm = realm::Realm::get_shared_realm(config);

View File

@ -195,7 +195,7 @@ struct Object {
template<typename P>
static ValueType validated_get_property(ContextType ctx, const ObjectType &object, const P &property, const char *message = nullptr) {
if (!has_property(ctx, object, property)) {
throw std::out_of_range(message ?: "Object missing expected property: " + util::to_string(property));
throw std::out_of_range(message ? message : "Object missing expected property: " + util::to_string(property));
}
return get_property(ctx, object, property);
}

View File

@ -31,7 +31,7 @@ template<typename T>
class RealmDelegate;
template<typename T>
static inline RealmDelegate<T> *get_delegate(Realm *realm) {
static inline RealmDelegate<T> *get_delegate(realm::Realm *realm) {
return static_cast<RealmDelegate<T> *>(realm->m_binding_context.get());
}
@ -59,19 +59,19 @@ static inline uint32_t validated_positive_index(std::string string) {
static inline void validate_argument_count(size_t count, size_t expected, const char *message = nullptr) {
if (count != expected) {
throw std::invalid_argument(message ?: "Invalid arguments");
throw std::invalid_argument(message ? message : "Invalid arguments");
}
}
static inline void validate_argument_count(size_t count, size_t min, size_t max, const char *message = nullptr) {
if (count < min || count > max) {
throw std::invalid_argument(message ?: "Invalid arguments");
throw std::invalid_argument(message ? message : "Invalid arguments");
}
}
static inline void validate_argument_count_at_least(size_t count, size_t expected, const char *message = nullptr) {
if (count < expected) {
throw std::invalid_argument(message ?: "Invalid arguments");
throw std::invalid_argument(message ? message : "Invalid arguments");
}
}

View File

@ -1,111 +0,0 @@
{
"variables": {
"use_realm_debug": "<!(echo $REALMJS_USE_DEBUG_CORE)"
},
"conditions": [
["OS=='mac'", {
"variables": {
"realm_enable_sync%": "1"
}
}, {
"variables": {
"realm_enable_sync%": "0"
}
}]
],
"targets": [
{
"target_name": "realm-core",
"type": "none",
"direct_dependent_settings": {
"conditions": [
["use_realm_debug!=''", {
"libraries": [ "-lrealm-node-dbg" ],
"defines": [ "REALM_DEBUG=1" ]
}, {
"libraries": [ "-lrealm-node" ]
}]
]
},
"all_dependent_settings": {
"defines": [ "REALM_HAVE_CONFIG", "REALM_PLATFORM_NODE=1", "REALM_ENABLE_SYNC=<(realm_enable_sync)" ]
},
"variables": {
"prefix": "<!(echo $REALM_CORE_PREFIX)"
},
"conditions": [
["prefix!=''", {
"all_dependent_settings": {
"include_dirs": [ "<(prefix)/src" ],
},
"direct_dependent_settings": {
"library_dirs": [ "<(prefix)/src/realm" ]
}
}, {
"dependencies": [ "vendored-realm" ]
}]
]
},
{
"target_name": "realm-sync",
"type": "none",
"dependencies": [ "realm-core" ], # sync headers include core headers
"direct_dependent_settings": {
"conditions": [
["use_realm_debug!=''", {
"libraries": [ "-lrealm-sync-node-dbg" ]
}, {
"libraries": [ "-lrealm-sync-node" ]
}]
]
},
"export_dependent_settings": [ "realm-core" ], # depending on sync is tantamount to depending on core
"variables": {
"prefix": "<!(echo $REALM_SYNC_PREFIX)"
},
"conditions": [
["prefix!=''", {
"all_dependent_settings": {
"include_dirs": [ "<(prefix)/src" ],
},
"direct_dependent_settings": {
"library_dirs": [ "<(prefix)/src/realm" ]
}
}, {
"dependencies": [ "vendored-realm" ]
}]
],
},
{
"variables": {
"vendor_dir%": "<(module_root_dir)/vendor",
},
"target_name": "vendored-realm",
"type": "none",
"conditions": [
["realm_enable_sync", {
"all_dependent_settings": {
"include_dirs": [ "<(module_root_dir)/vendor/node-sync/include" ],
"library_dirs": [ "<(module_root_dir)/vendor/node-sync/osx" ]
}
}, {
"all_dependent_settings": {
"include_dirs": [ "<(module_root_dir)/vendor/core-node/include" ],
"library_dirs": [ "<(module_root_dir)/vendor/core-node" ]
},
}],
["realm_download_binaries", {
"actions": [
{
"action_name": "download-realm",
"inputs": [ ],
"outputs": [ "<(module_root_dir)/vendor/core-node" ],
"action": [ "<(module_root_dir)/scripts/download-core.sh", "node", "<(realm_enable_sync)" ]
}
]
}]
]
}
]
}

View File

@ -182,12 +182,12 @@ inline v8::Local<v8::FunctionTemplate> ObjectWrap<ClassType>::create_template()
if (s_class.index_accessor.getter) {
auto &index_accessor = s_class.index_accessor;
instance_tpl->SetIndexedPropertyHandler(index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes);
instance_tpl->SetIndexedPropertyHandler(index_accessor.getter, index_accessor.setter ? index_accessor.setter : set_readonly_index, 0, 0, get_indexes);
}
if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) {
// Use our own wrapper for the setter since we want to throw for negative indices.
auto &string_accessor = s_class.string_accessor;
instance_tpl->SetNamedPropertyHandler(string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
instance_tpl->SetNamedPropertyHandler(string_accessor.getter ? string_accessor.getter : get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
}
return scope.Escape(tpl);
@ -219,7 +219,7 @@ inline void ObjectWrap<ClassType>::setup_property(v8::Local<TargetType> target,
v8::Local<v8::String> prop_name = Nan::New(name).ToLocalChecked();
v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete);
target->SetAccessor(prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
target->SetAccessor(prop_name, property.getter, property.setter ? property.setter : set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
}
template<typename ClassType>

View File

@ -16,21 +16,47 @@
//
////////////////////////////////////////////////////////////////////////////
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <system_error>
#include <stdexcept>
#include <vector>
#include <uv.h>
#include "../platform.hpp"
namespace realm {
class UVException : public std::runtime_error {
public:
UVException(uv_errno_t error)
: std::runtime_error(uv_strerror(error))
, m_error(error)
{ }
const uv_errno_t m_error;
};
struct FileSystemRequest : uv_fs_t {
~FileSystemRequest() {
uv_fs_req_cleanup(this);
}
};
// taken from Node.js: function Cwd in node.cc
std::string default_realm_file_directory()
{
// Relative paths should always be relative to the current working directory.
return ".";
#ifdef _WIN32
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
char buf[MAX_PATH * 4];
#else
char buf[PATH_MAX];
#endif
size_t cwd_len = sizeof(buf);
int err = uv_cwd(buf, &cwd_len);
if (err) {
throw UVException(static_cast<uv_errno_t>(err));
}
return std::string(buf, cwd_len);
}
void ensure_directory_exists_for_file(const std::string &file_path)
@ -45,8 +71,9 @@ void ensure_directory_exists_for_file(const std::string &file_path)
}
std::string dir_path = file_path.substr(0, pos++);
if (mkdir(dir_path.c_str(), 0755) != 0 && errno != EEXIST) {
throw std::system_error(errno, std::system_category());
FileSystemRequest req;
if (uv_fs_mkdir(uv_default_loop(), &req, dir_path.c_str(), 0755, nullptr) < 0 && req.result != UV_EEXIST) {
throw UVException(static_cast<uv_errno_t>(req.result));
}
}
}
@ -56,10 +83,55 @@ void copy_bundled_realm_files()
throw std::runtime_error("Realm for Node does not support this method.");
}
inline bool ends_with(const std::string& str, const std::string& suffix) {
return str.size() > suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
void remove_realm_files_from_directory(const std::string &dir_path)
{
std::string delete_realms = "rm -rf '" + dir_path + "'/*.realm*";
system(delete_realms.c_str());
FileSystemRequest scandir_req;
if (uv_fs_scandir(uv_default_loop(), &scandir_req, dir_path.c_str(), 0, nullptr) < 0) {
throw UVException(static_cast<uv_errno_t>(scandir_req.result));
}
uv_dirent_t entry;
while (uv_fs_scandir_next(&scandir_req, &entry) != UV_EOF) {
std::string path(dir_path + '/' + entry.name);
if (entry.type == UV_DIRENT_DIR) {
static std::string realm_management_extension(".realm.management");
if (ends_with(path, realm_management_extension)) {
uv_dirent_t management_entry;
FileSystemRequest management_scandir_req;
if (uv_fs_scandir(uv_default_loop(), &management_scandir_req, path.c_str(), 0, nullptr) < 0) {
throw UVException(static_cast<uv_errno_t>(scandir_req.result));
}
while (uv_fs_scandir_next(&management_scandir_req, &management_entry) != UV_EOF) {
std::string management_entry_path = path + '/' + management_entry.name;
FileSystemRequest delete_req;
if (uv_fs_unlink(uv_default_loop(), &delete_req, management_entry_path.c_str(), nullptr) != 0) {
throw UVException(static_cast<uv_errno_t>(delete_req.result));
}
}
FileSystemRequest management_rmdir_req;
if (uv_fs_rmdir(uv_default_loop(), &management_rmdir_req, path.c_str(), nullptr)) {
throw UVException(static_cast<uv_errno_t>(management_rmdir_req.result));
}
}
} else {
static std::string realm_extension(".realm");
static std::string realm_note_extension(".realm.note");
static std::string realm_lock_extension(".realm.lock");
if (ends_with(path, realm_extension) || ends_with(path, realm_note_extension) || ends_with(path, realm_lock_extension)) {
FileSystemRequest delete_req;
if (uv_fs_unlink(uv_default_loop(), &delete_req, path.c_str(), nullptr) != 0) {
throw UVException(static_cast<uv_errno_t>(delete_req.result));
}
}
}
}
}
} // realm

@ -1 +1 @@
Subproject commit 5a00a269ffcfbcc4c27b40a6dcf761f9b54fcc02
Subproject commit 2f47170b380fa3ee50d2198cfc56940966c3aa1e

View File

@ -24,6 +24,9 @@
"<!(node -e \"require('nan')\")"
],
"conditions": [
["OS=='win'", {
"defines": [ "_UNICODE", "UNICODE", "WIN32=1", "_HAS_EXCEPTIONS=1" ]
}],
["OS=='mac'", {
"xcode_settings": {
"CLANG_CXX_LANGUAGE_STANDARD": "c++14",
@ -35,6 +38,31 @@
"WARNING_CFLAGS": [ "<@(warning-flags)" ]
}
}]
]
],
# windows stuff
"configurations": {
"Debug": {
"msvs_settings": {
"VCCLCompilerTool": {
"RuntimeTypeInfo": "true",
"AdditionalOptions": [ "/MDd" ]
},
}
},
"Release": {
"msvs_settings": {
"VCCLCompilerTool": {
"RuntimeTypeInfo": "true",
"AdditionalOptions": [ "/MD" ]
},
}
}
},
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1
}
},
"msvs_disabled_warnings": [ 4068, 4101, 4244, 4996 ],
}
}

View File

@ -36,9 +36,9 @@ function createNotificationTest(config, getObservable, addListener, removeListen
reject(new Error('Timed out waiting for change notification'));
}, 5000);
let cleanup = () => {
let cleanup = (cb) => {
clearTimeout(timer);
worker.terminate();
worker.terminate(cb);
};
var count = 0;
@ -49,17 +49,17 @@ function createNotificationTest(config, getObservable, addListener, removeListen
worker.onmessage = (message) => {
if (message.error) {
reject(message.error);
cleanup();
cleanup(() => reject(message.error));
}
else if (message.result == 'resolve') {
if (count != expectedCount) {
cleanup(() => {
if (count !== expectedCount) {
reject('Notification count ' + count + ' not equal to expected count ' + expectedCount);
}
else {
resolve();
}
cleanup();
});
}
else {
if (message.result == 'removeListener') {

View File

@ -56,4 +56,17 @@ module.exports = {
realm = new Realm({schema: [Schemas.TestObject], encryptionKey: key});
TestCase.assertEqual(realm.objects('TestObject').length, 1);
},
testRealmSchemaVersion: function() {
var encryptionKey = new Int8Array(64);
var realm = new Realm({schema: [], schemaVersion: 3, path: 'encrypted.realm', encryptionKey: encryptionKey});
TestCase.assertEqual(realm.schemaVersion, 3);
TestCase.assertEqual(Realm.schemaVersion('encrypted.realm', encryptionKey), 3);
TestCase.assertThrows(function() {
Realm.schemaVersion('encrypted.realm', encryptionKey, 'extra');
});
TestCase.assertThrows(function() {
Realm.schemaVersion('encrypted.realm', 'asdf');
});
},
};

View File

@ -26,10 +26,14 @@ var TESTS = {
RealmTests: require('./realm-tests'),
ResultsTests: require('./results-tests'),
QueryTests: require('./query-tests'),
EncryptionTests: require('./encryption-tests'),
MigrationTests: require('./migration-tests')
};
// encryption is not supported on windows
if (!(typeof process === 'object' && process.platform === 'win32')) {
TESTS.EncryptionTests = require('./encryption-tests');
}
// If sync is enabled, run the user tests
if (Realm.Sync) {
TESTS.UserTests = require('./user-tests');

View File

@ -22,6 +22,11 @@ var Realm = require('realm');
var TestCase = require('./asserts');
var schemas = require('./schemas');
let pathSeparator = '/';
if (typeof process === 'object' && process.platform === 'win32') {
pathSeparator = '\\';
}
module.exports = {
testRealmConstructor: function() {
var realm = new Realm({schema: []});
@ -45,7 +50,7 @@ module.exports = {
var defaultRealm2 = new Realm();
TestCase.assertEqual(defaultRealm2.path, Realm.defaultPath);
var defaultDir = Realm.defaultPath.substring(0, Realm.defaultPath.lastIndexOf("/") + 1)
var defaultDir = Realm.defaultPath.substring(0, Realm.defaultPath.lastIndexOf(pathSeparator) + 1)
var testPath = 'test1.realm';
var realm = new Realm({schema: [], path: testPath});
TestCase.assertEqual(realm.path, defaultDir + testPath);
@ -150,7 +155,7 @@ module.exports = {
TestCase.assertEqual(defaultRealm.path, Realm.defaultPath);
try {
var newPath = Realm.defaultPath.substring(0, defaultPath.lastIndexOf('/') + 1) + 'default2.realm';
var newPath = Realm.defaultPath.substring(0, defaultPath.lastIndexOf(pathSeparator) + 1) + 'default2.realm';
Realm.defaultPath = newPath;
defaultRealm = new Realm({schema: []});
TestCase.assertEqual(defaultRealm.path, newPath, "should use updated default realm path");
@ -170,18 +175,6 @@ module.exports = {
realm = new Realm({schema: [], schemaVersion: 2, path: 'another.realm'});
TestCase.assertEqual(realm.schemaVersion, 2);
TestCase.assertEqual(Realm.schemaVersion('another.realm'), 2);
var encryptionKey = new Int8Array(64);
realm = new Realm({schema: [], schemaVersion: 3, path: 'encrypted.realm', encryptionKey: encryptionKey});
TestCase.assertEqual(realm.schemaVersion, 3);
TestCase.assertEqual(Realm.schemaVersion('encrypted.realm', encryptionKey), 3);
TestCase.assertThrows(function() {
Realm.schemaVersion('encrypted.realm', encryptionKey, 'extra');
});
TestCase.assertThrows(function() {
Realm.schemaVersion('encrypted.realm', 'asdf');
});
},
testRealmWrite: function() {

View File

@ -35,10 +35,17 @@ class Worker {
this._process.send(message);
}
}
terminate() {
terminate(cb) {
if (!cb) {
cb = function() { };
}
if (this._process) {
this._process.once('close', cb);
this._process.kill();
delete this._process;
} else {
cb();
}
}
}

View File

@ -58,13 +58,13 @@ for (const suiteName in tests) {
try {
let result = RealmTests.runTest(suiteName, testName);
if (result instanceof Promise) {
result.then(done, fail);
result.then(done, done.fail.bind(done));
} else {
done();
}
}
catch (e) {
fail(e);
done.fail(e);
}
});
}