├── lib └── ios │ └── Cartfile ├── test ├── www │ ├── jxcore │ │ ├── .gitignore │ │ ├── lib │ │ │ ├── utils │ │ │ │ ├── Promise.js │ │ │ │ ├── process.js │ │ │ │ └── asserts.js │ │ │ ├── parsePlatformArg.js │ │ │ ├── sinonTest.js │ │ │ ├── testLogger.js │ │ │ ├── thaliTape.js │ │ │ ├── MockMobile.js │ │ │ ├── testLoader.js │ │ │ └── CoordinatedTape.js │ │ ├── bv_tests │ │ │ ├── disabled │ │ │ │ ├── pkcs12folderbad │ │ │ │ │ └── pkcs12.pfx │ │ │ │ ├── CITestClass.js │ │ │ │ ├── TestThaliMobileNative │ │ │ │ │ ├── QuitSignal.js │ │ │ │ │ └── Message.js │ │ │ │ ├── IdentityExchange │ │ │ │ │ ├── identityExchangeTestUtils.js │ │ │ │ │ ├── testIdentityExchangeUtils.js │ │ │ │ │ ├── testConnectionTable.js │ │ │ │ │ └── testLiveIdentityExchange.js │ │ │ │ ├── mockmobile.js │ │ │ │ ├── testMultiplex.js │ │ │ │ ├── testThaliManagerStopCoordinated.js │ │ │ │ └── testThaliCryptoManager.js │ │ │ ├── testUsn.js │ │ │ ├── testNativeMethod.js │ │ │ └── testPouchDBGenerator.js │ │ ├── cleanme.sh │ │ ├── meta_tests │ │ │ ├── disabled │ │ │ │ ├── testPerfTestFramework.js │ │ │ │ └── testPerfTestFrameworkClient.js │ │ │ ├── testTestUtils.js │ │ │ ├── testInstall.js │ │ │ ├── testCanBeSkipped.js │ │ │ ├── testPouchDBAgent.js │ │ │ ├── testResultsProcessor.js │ │ │ ├── testSync.js │ │ │ └── testTestSendData.js │ │ ├── public │ │ │ └── index.css │ │ ├── views │ │ │ └── ejs │ │ │ │ └── index.ejs │ │ ├── config.js │ │ ├── perf_tests │ │ │ ├── disabled │ │ │ │ └── testNewFindPeers.js │ │ │ ├── ReConnectTCPServer.js │ │ │ ├── SendDataTCPServer.js │ │ │ └── BluetoothConnectionLimits.js │ │ ├── package.json │ │ ├── readme.md │ │ ├── UnitTest_app.js │ │ ├── PerfTest_app.js │ │ ├── CITestMode.js │ │ └── runTests.js │ ├── index.html │ └── js │ │ └── thali_main.js └── TestServer │ ├── utils │ ├── Promise.js │ ├── process.js │ ├── ThaliLogger.js │ └── asserts.js │ ├── config │ ├── Socket.js │ ├── TestDevice.js │ └── UnitTest.js │ ├── package.json │ ├── generateServerAddress.js │ ├── TestPerfTestConfig.js │ ├── PerfTestConfig.js │ ├── TestPerfTestConfig2.js │ ├── index.js │ ├── IPAddressToFile.js │ └── HttpServer.js ├── thali ├── install │ ├── utils │ │ ├── Promise.js │ │ ├── process.js │ │ └── child_process.js │ ├── SSDPReplacer │ │ ├── plugin.xml │ │ ├── package.json │ │ └── process.js │ ├── postPublishThaliCordovaPlugin.js │ ├── package.json │ ├── prePublishThaliCordovaPlugin.js │ ├── include.sh │ │ └── build-dep.sh │ ├── setUpDesktop.sh │ ├── cordova-hooks │ │ └── ios │ │ │ ├── before_plugin_install.js │ │ │ └── after_plugin_install.js │ └── ios │ │ ├── build-ci-no-tests.xcconfig │ │ └── build-ci.xcconfig ├── conf.json ├── NextGeneration │ ├── utils │ │ ├── leveldownMobileAdapter.js │ │ ├── platform.js │ │ ├── usn.js │ │ ├── common.js │ │ └── pouchDBCheckpointsPlugin.js │ ├── security │ │ └── hkdf.js │ ├── identityExchange │ │ └── connectionTable.js │ ├── thaliConfig.js │ ├── notification │ │ └── thaliPskMapCache.js │ └── makeIntoCloseAllServer.js ├── validations.js ├── installCordovaPlugin.js ├── ThaliLogger.js ├── package.json ├── thalicryptomanager.js └── thaliemitter.js ├── src ├── android │ ├── test │ │ ├── io │ │ │ └── jxcore │ │ │ │ └── node │ │ │ │ ├── JXcoreThaliCallbackMock.java │ │ │ │ ├── InputStreamMock.java │ │ │ │ ├── OutputStreamMock.java │ │ │ │ ├── CITestClass.java │ │ │ │ ├── SocketThreadBaseMock.java │ │ │ │ ├── ListenerMock.java │ │ │ │ ├── IncomingSocketThreadMock.java │ │ │ │ └── OutgoingSocketThreadMock.java │ │ ├── com │ │ │ └── test │ │ │ │ └── thalitest │ │ │ │ └── ThaliTestSuite.java │ │ └── build-extras.gradle │ ├── java │ │ ├── build-extras.gradle │ │ └── io │ │ │ └── jxcore │ │ │ └── node │ │ │ ├── ConnectionData.java │ │ │ ├── WifiLocker.java │ │ │ ├── SurroundingStateObserver.java │ │ │ ├── JXcoreThaliCallback.java │ │ │ └── ListenerOrIncomingConnection.java │ ├── JXcore.gradle │ └── project.properties └── ios │ └── Testing │ ├── TestRunnerProtocol.swift │ └── AppContext+TestRuner.swift ├── mobile_test.json ├── appveyor.yml ├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── LICENSE ├── .gitignore ├── www └── android │ └── thaliPermissions.js └── .eslintrc.js /lib/ios/Cartfile: -------------------------------------------------------------------------------- 1 | github "thaliproject/thali-ios" "master" 2 | -------------------------------------------------------------------------------- /test/www/jxcore/.gitignore: -------------------------------------------------------------------------------- 1 | /customPouchDir/ 2 | /customPouchServerDir/ 3 | -------------------------------------------------------------------------------- /thali/install/utils/Promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | 5 | module.exports = Promise; 6 | -------------------------------------------------------------------------------- /test/TestServer/utils/Promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | 5 | module.exports = Promise; 6 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/utils/Promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | 5 | module.exports = Promise; 6 | -------------------------------------------------------------------------------- /test/TestServer/config/Socket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | retryCount: 120, 5 | retryTimeout: 1 * 1000 6 | }; 7 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/JXcoreThaliCallbackMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | public class JXcoreThaliCallbackMock extends JXcoreThaliCallback{ 4 | } 5 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/pkcs12folderbad/pkcs12.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaliproject/Thali_CordovaPlugin/HEAD/test/www/jxcore/bv_tests/disabled/pkcs12folderbad/pkcs12.pfx -------------------------------------------------------------------------------- /test/TestServer/config/TestDevice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | setupTimeout: 1 * 60 * 1000, 5 | testTimeout: 3 * 60 * 1000, 6 | teardownTimeout: 1 * 60 * 1000 7 | }; 8 | -------------------------------------------------------------------------------- /thali/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["plugins/markdown"], 3 | "opts": { "recurse": true }, 4 | "source": { 5 | "exclude": [ "node_modules", "install"], 6 | "includePattern": ".+\\.js?$" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/android/java/build-extras.gradle: -------------------------------------------------------------------------------- 1 | ext.postBuildExtras = { 2 | android { 3 | compileOptions { 4 | sourceCompatibility JavaVersion.VERSION_1_6 5 | targetCompatibility JavaVersion.VERSION_1_6 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/android/test/com/test/thalitest/ThaliTestSuite.java: -------------------------------------------------------------------------------- 1 | package com.test.thalitest; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | 6 | @RunWith(Suite.class) 7 | @Suite.SuiteClasses({ 8 | }) 9 | 10 | public class ThaliTestSuite { 11 | } 12 | -------------------------------------------------------------------------------- /thali/install/SSDPReplacer/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | SSDP replacer hook 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/android/JXcore.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenLocal() 3 | maven { 4 | url "https://bintray.com/artifact/download/thali/Thali" 5 | } 6 | } 7 | 8 | dependencies { 9 | compile(group: 'org.thaliproject.p2p.btconnectorlib', name: 'btconnectorlib2', version: btconnectorlib2Version, ext: 'aar') 10 | } 11 | -------------------------------------------------------------------------------- /test/www/jxcore/cleanme.sh: -------------------------------------------------------------------------------- 1 | find ./node_modules -name \*.o -exec rm {} \; 2 | find ./node_modules -name \*.a -exec rm {} \; 3 | find ./node_modules -name \*.c -exec rm {} \; 4 | find ./node_modules -name \*.cc -exec rm {} \; 5 | find ./node_modules -name \*.d -exec rm {} \; 6 | find ./node_modules -name \*.gif -exec rm {} \; 7 | find ./node_modules -name \*.html -exec rm {} \; 8 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/CITestClass.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tape = require('../lib/thaliTape'); 4 | var test = tape({ 5 | 6 | setup: function (t) { 7 | t.end(); 8 | }, 9 | teardown: function (t) { 10 | t.end(); 11 | } 12 | }); 13 | 14 | test('The test that always pass', function (t) { 15 | t.ok(true); 16 | t.end(); 17 | }); 18 | -------------------------------------------------------------------------------- /src/ios/Testing/TestRunnerProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestRunnerProtocol.swift 3 | // Thali 4 | // 5 | // Copyright (C) Microsoft. All rights reserved. 6 | // Licensed under the MIT license. 7 | // See LICENSE.txt file in the project root for full license information. 8 | // 9 | 10 | @objc 11 | protocol TestRunnerProtocol { 12 | 13 | func runNativeTests() -> String 14 | } 15 | -------------------------------------------------------------------------------- /mobile_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": "./build.sh", 3 | "binary_path": { 4 | "ios": "ThaliTest.app", 5 | "android": "android-release-unsigned.apk" 6 | }, 7 | "target": "all", 8 | "priority": "normal", 9 | "csname": { 10 | "android": "com.thaliproject.thalitest", 11 | "ios": "com.thaliproject.thalitest" 12 | }, 13 | "timeout": 1400, 14 | "serverScript": "test/TestServer/" 15 | } 16 | -------------------------------------------------------------------------------- /test/TestServer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thali-test-server", 3 | "version": "0.0.1", 4 | "description": "Thali Test Server App - to be run under CI", 5 | "dependencies": { 6 | "express": "4.13.3", 7 | "fs-extra-promise": "0.2.0", 8 | "bluebird": "3.4.6", 9 | "socket.io": "1.4.8", 10 | "winston": "2.2.0", 11 | "object-assign": "4.1.0", 12 | "node-uuid": "1.4.7" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/TestServer/generateServerAddress.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var iPAddressToFile = require('./IPAddressToFile'); 4 | 5 | /** 6 | * A quick little command line utility to allow us to generate the server config 7 | * before creating a Cordova test project. 8 | */ 9 | 10 | iPAddressToFile(process.argv[2]).then(function () { 11 | process.exit(0); 12 | }).catch(function (err) { 13 | console.log(err); 14 | process.exit(1); 15 | }); 16 | -------------------------------------------------------------------------------- /thali/install/SSDPReplacer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SSDPReplacer", 3 | "version": "0.0.1", 4 | "description": "Used to replace hardcoded SSDP values", 5 | "main": "hook.js", 6 | "dependencies": { 7 | "bluebird": "3.4.6", 8 | "end-with": "1.0.2", 9 | "findit": "2.0.0", 10 | "randomstring": "1.1.5", 11 | "node-uuid": "1.4.7", 12 | "fs-extra-promise": "0.2.0" 13 | }, 14 | "author": "Microsoft", 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/InputStreamMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | public class InputStreamMock extends InputStream { 7 | public boolean isClosed = false; 8 | 9 | @Override 10 | public int read() throws IOException { 11 | return 0; 12 | } 13 | 14 | @Override 15 | public void close() throws IOException { 16 | isClosed = true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/OutputStreamMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public class OutputStreamMock extends OutputStream { 7 | public boolean isClosed = false; 8 | 9 | @Override 10 | public void write(int oneByte) throws IOException { 11 | 12 | } 13 | 14 | @Override 15 | public void close() throws IOException { 16 | isClosed = true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | os: Visual Studio 2015 2 | clone_folder: C:\Thali_CordovaPlugin 3 | install: 4 | - npm install -g cordova@6.1.0 5 | - appveyor DownloadFile http://jxcore.azureedge.net/jxcore/0312/release/jx_win64v8.zip 6 | - jar xf jx_win64v8.zip 7 | - SET PATH=%PATH%;C:\Thali_CordovaPlugin\jx_win64v8 8 | - SET NVM_NODEJS_ORG_MIRROR=https://jxcore.azureedge.net 9 | - SET JX_NPM_JXB=jxb311 10 | build_script: 11 | - '"C:\Program Files\Git\bin\sh.exe" --login -c "./build.sh"' 12 | test: off 13 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/CITestClass.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import org.junit.Test; 4 | import static org.hamcrest.CoreMatchers.is; 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.core.IsNull.notNullValue; 7 | 8 | public class CITestClass { 9 | @Test 10 | public void test() throws Exception { 11 | String str = "TestingString"; 12 | assertThat("String is not null", str, is(notNullValue())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/TestServer/TestPerfTestConfig.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | 3 | userConfig : { 4 | "ios" : { 5 | "startTimeout": 3000, 6 | }, 7 | "android" : { 8 | "startTimeout": 3000 9 | } 10 | }, 11 | 12 | testConfig : [ 13 | { 14 | "name": "testSendData.js", 15 | "serverTimeout": 15000, 16 | "timeout": 1000000, 17 | "rounds": 1, 18 | "dataTimeout": 10000, 19 | "dataAmount": 100000, 20 | "conReTryTimeout": 5000, 21 | "conReTryCount": 5 22 | } 23 | ] 24 | }; 25 | 26 | module.exports = config; 27 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/SocketThreadBaseMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import android.bluetooth.BluetoothSocket; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | 9 | public class SocketThreadBaseMock extends SocketThreadBase { 10 | public SocketThreadBaseMock(BluetoothSocket bluetoothSocket, Listener listener, 11 | InputStream inputStream, OutputStream outputStream) 12 | throws IOException { 13 | super(bluetoothSocket, listener, inputStream, outputStream); 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/parsePlatformArg.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function () { 4 | var platform = require('thali/NextGeneration/utils/platform'); 5 | var argv = require('minimist')(process.argv.slice(2)); 6 | if (!argv.platform) { 7 | return null; 8 | } 9 | switch (argv.platform) { 10 | case platform.names.IOS: 11 | case platform.names.ANDROID: { 12 | return argv.platform; 13 | } 14 | default: { 15 | throw new Error('Unrecognized platform: ' + argv.platform + '. ' + 16 | 'Available platforms: ' + JSON.stringify(platform.names)); 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /thali/install/utils/process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | process 5 | .on('SIGINT', function () { 6 | console.error('got \'SIGINT\', terminating'); 7 | process.exit(130); // Ctrl-C std exit code 8 | }) 9 | .on('uncaughtException', function (error) { 10 | console.error( 11 | 'uncaught exception, error: \'%s\', stack: \'%s\'', 12 | error.toString(), error.stack 13 | ); 14 | process.exit(1); 15 | }) 16 | .on('unhandledRejection', function (error, p) { 17 | console.error( 18 | 'uncaught promise rejection, error: \'%s\', stack: \'%s\'', 19 | error.toString(), error.stack 20 | ); 21 | process.exit(2); 22 | }); 23 | -------------------------------------------------------------------------------- /src/android/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-21 15 | -------------------------------------------------------------------------------- /thali/install/SSDPReplacer/process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | process 5 | .on('SIGINT', function () { 6 | console.error('got \'SIGINT\', terminating'); 7 | process.exit(130); // Ctrl-C std exit code 8 | }) 9 | .on('uncaughtException', function (error) { 10 | console.error( 11 | 'uncaught exception, error: \'%s\', stack: \'%s\'', 12 | error.toString(), error.stack 13 | ); 14 | process.exit(1); 15 | }) 16 | .on('unhandledRejection', function (error, p) { 17 | console.error( 18 | 'uncaught promise rejection, error: \'%s\', stack: \'%s\'', 19 | error.toString(), error.stack 20 | ); 21 | process.exit(2); 22 | }); 23 | -------------------------------------------------------------------------------- /thali/NextGeneration/utils/leveldownMobileAdapter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var CoreLeveldownAdapter = require('pouchdb-adapter-leveldb-core'); 4 | 5 | function LeveldownMobileAdapter(opts, callback) { 6 | if (!opts.db) { 7 | return callback(new Error('leveldown is not defined')); 8 | } 9 | 10 | CoreLeveldownAdapter.call(this, opts, callback); 11 | } 12 | 13 | // overrides for normal LevelDB behavior on Node 14 | LeveldownMobileAdapter.valid = function () { 15 | return true; 16 | }; 17 | LeveldownMobileAdapter.use_prefix = false; 18 | 19 | module.exports = function (PouchDB) { 20 | PouchDB.adapter('leveldb-mobile', LeveldownMobileAdapter, true); 21 | }; 22 | -------------------------------------------------------------------------------- /thali/install/postPublishThaliCordovaPlugin.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE.txt file in the project root 3 | // for full license information. 4 | // 5 | 6 | 'use strict'; 7 | 8 | var fs = require('fs'); 9 | var path = require('path'); 10 | 11 | // We don't want to checkin the readme.md in the local directory 12 | // since it is just a copy of the one in the parent directory 13 | // so we clean it up after publishing so we don't accidentally 14 | // check it in. 15 | 16 | var readMeFileName = 'readme.md'; 17 | var localReadMe = path.join(__dirname, '..', readMeFileName); 18 | fs.unlinkSync(localReadMe); 19 | process.exit(0); 20 | -------------------------------------------------------------------------------- /test/TestServer/utils/process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var logger = require('./ThaliLogger')('TestServerProcess'); 4 | 5 | 6 | process 7 | .on('SIGINT', function () { 8 | logger.error('got \'SIGINT\', terminating'); 9 | process.exit(130); // Ctrl-C std exit code 10 | }) 11 | .on('uncaughtException', function (error) { 12 | logger.error( 13 | 'uncaught exception, error: \'%s\', stack: \'%s\'', 14 | error.toString(), error.stack 15 | ); 16 | process.exit(1); 17 | }) 18 | .on('unhandledRejection', function (error, p) { 19 | logger.error( 20 | 'uncaught promise rejection, error: \'%s\', stack: \'%s\'', 21 | error.toString(), error.stack 22 | ); 23 | process.exit(2); 24 | }); 25 | -------------------------------------------------------------------------------- /test/TestServer/config/UnitTest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Default amount of devices required to run tests. 4 | 5 | module.exports = { 6 | devices: { 7 | // This is a list of required platforms. 8 | // All required platform should have minDevices entry. 9 | // So all required platforms should be listed in desired platform list. 10 | ios: -1, 11 | android: -1, 12 | desktop: -1 13 | }, 14 | minDevices: { 15 | // This is a list of desired platforms. 16 | ios: 3, 17 | android: 3, 18 | desktop: 3 19 | }, 20 | // if 'devices[platform]' is -1 we wont limit the amount of devices. 21 | // We will wait some amount of time before tests. 22 | waiting_for_devices_timeout: 5 * 1000 23 | }; 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.java] 17 | indent_style = space 18 | indent_size = 4 19 | 20 | [*.js] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.hbs] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.css] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [*.html] 33 | indent_style = space 34 | indent_size = 2 35 | 36 | [*.{diff,md}] 37 | trim_trailing_whitespace = false 38 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/utils/process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var logger = require('thali/ThaliLogger')('TestsProcess'); 4 | 5 | 6 | process 7 | .on('SIGINT', function () { 8 | logger.error('got \'SIGINT\', terminating'); 9 | process.exit(130); // Ctrl-C std exit code 10 | }) 11 | .on('uncaughtException', function (error) { 12 | logger.error( 13 | 'uncaught exception, error: \'%s\', stack: \'%s\'', 14 | error.toString(), error.stack 15 | ); 16 | logger.error('****TEST_LOGGER:[PROCESS_ON_EXIT_FAILED]****'); 17 | process.exit(1); 18 | }) 19 | .on('unhandledRejection', function (error) { 20 | logger.error( 21 | 'uncaught promise rejection, error: \'%s\', stack: \'%s\'', 22 | error.toString(), error.stack 23 | ); 24 | logger.error('****TEST_LOGGER:[PROCESS_ON_EXIT_FAILED]****'); 25 | process.exit(2); 26 | }); 27 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/ListenerMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | public class ListenerMock implements SocketThreadBase.Listener { 4 | 5 | @Override 6 | public void onListeningForIncomingConnections(int portNumber) { 7 | 8 | } 9 | 10 | @Override 11 | public void onDataTransferred(int numberOfBytes) { 12 | 13 | } 14 | 15 | @Override 16 | public void onDisconnected(SocketThreadBase who, String errorMessage) { 17 | 18 | } 19 | 20 | @Override 21 | public void onDisconnected(SocketThreadBase who, Exception exception) { 22 | 23 | } 24 | 25 | @Override 26 | public void onTransferError(SocketThreadBase who, String errorMessage) { 27 | 28 | } 29 | 30 | @Override 31 | public void onDone(SocketThreadBase who, boolean threadDoneWasSending) { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/TestServer/PerfTestConfig.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | 3 | userConfig : { 4 | "ios" : { 5 | "startTimeout": 120000 6 | }, 7 | "android" : { 8 | "startTimeout": 120000 9 | } 10 | }, 11 | 12 | testConfig : [ 13 | { 14 | "name": "testFindPeers.js", 15 | "serverTimeout": 120000, 16 | "timeout": 100000, 17 | "rounds": 1, 18 | "dataTimeout": 10000, 19 | "dataAmount": 100000, 20 | "conReTryTimeout": 5000, 21 | "conReTryCount": 5 22 | }, 23 | { 24 | "name": "testSendData.js", 25 | "serverTimeout": 120000, 26 | "timeout": 100000, 27 | "rounds": 1, 28 | "dataTimeout": 20000, 29 | "dataAmount": 100000, 30 | "conReTryTimeout": 5000, 31 | "conReTryCount": 5 32 | } 33 | ] 34 | }; 35 | 36 | module.exports = config; 37 | -------------------------------------------------------------------------------- /test/TestServer/TestPerfTestConfig2.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | 3 | userConfig : { 4 | "ios" : { 5 | "startTimeout": 3000, 6 | }, 7 | "android" : { 8 | "startTimeout": 3000 9 | } 10 | }, 11 | 12 | testConfig : [ 13 | { 14 | "name": "testSendData.js", 15 | "serverTimeout": 15000, 16 | "timeout": 1000000, 17 | "rounds": 1, 18 | "dataTimeout": 10000, 19 | "dataAmount": 100000, 20 | "conReTryTimeout": 5000, 21 | "conReTryCount": 5 22 | }, 23 | { 24 | "name": "testFindPeers.js", 25 | "serverTimeout": 15000, 26 | "timeout": 1000000, 27 | "rounds": 1, 28 | "dataTimeout": 10000, 29 | "dataAmount": 100000, 30 | "conReTryTimeout": 5000, 31 | "conReTryCount": 5 32 | } 33 | ] 34 | }; 35 | 36 | module.exports = config; 37 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/sinonTest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var sinon = require('sinon'); 4 | 5 | function sinonTest (callback) { 6 | return function (t) { 7 | var config = sinon.getConfig(sinon.config); 8 | config.injectInto = config.injectIntoThis && this || config.injectInto; 9 | var sandbox = sinon.sandbox.create(config); 10 | var args = Array.prototype.slice.call(arguments); 11 | var ok; 12 | 13 | if (typeof t.end === 'function') { 14 | t.on('result', function(res) { 15 | ok = res.ok; 16 | }); 17 | 18 | t.on('end', function() { 19 | if (!ok) { 20 | sandbox.restore(); 21 | } else { 22 | sandbox.verifyAndRestore(); 23 | } 24 | }); 25 | } 26 | 27 | return callback.apply(this, args.concat(sandbox.args)); 28 | }; 29 | } 30 | 31 | module.exports = sinonTest; 32 | -------------------------------------------------------------------------------- /src/android/java/io/jxcore/node/ConnectionData.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | 4 | import org.thaliproject.p2p.btconnectorlib.PeerProperties; 5 | 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | public class ConnectionData { 9 | public static AtomicInteger INDEX_ID = new AtomicInteger(1); 10 | public final PeerProperties peerProperties; 11 | public final int id; 12 | public final boolean isIncoming; 13 | 14 | public ConnectionData(PeerProperties peerProperties, boolean isIncoming) { 15 | this.peerProperties = peerProperties; 16 | this.isIncoming = isIncoming; 17 | this.id = INDEX_ID.getAndAdd(1); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "Peer properties: " + peerProperties.toString() + ".\n Is incomming connection: " + 23 | isIncoming + ".\n id: " + id; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ios/Testing/AppContext+TestRuner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppContext+TestRuner.swift 3 | // Thali 4 | // 5 | // Copyright (C) Microsoft. All rights reserved. 6 | // Licensed under the MIT license. 7 | // See LICENSE.txt file in the project root for full license information. 8 | // 9 | 10 | import Foundation 11 | import SwiftXCTest 12 | import ThaliCore 13 | 14 | extension AppContext: TestRunnerProtocol { 15 | 16 | func runNativeTests() -> String { 17 | let rootTestSuite = XCTestSuite(name: "All tests") 18 | 19 | let currentTestSuite = XCTestSuite( 20 | name: "All tests", 21 | testCases: [ 22 | [testCase(AppContextTests.allTests)] 23 | ].flatMap { $0 } 24 | ) 25 | 26 | rootTestSuite.addTest(currentTestSuite) 27 | 28 | let runner = TestRunner(testSuite: rootTestSuite) 29 | runner.runTest() 30 | return runner.resultDescription ?? "" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/android/test/build-extras.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile fileTree(dir: 'libs', include: ['*.jar']) 3 | compile 'com.android.support:appcompat-v7:22.2.0' 4 | compile 'junit:junit:4.12' 5 | 6 | compile 'com.android.support:support-annotations:22.2.0' 7 | compile 'com.android.support.test:runner:0.3' 8 | compile 'com.android.support.test:rules:0.3' 9 | compile 'com.android.support.test.espresso:espresso-core:2.2' 10 | compile 'org.mockito:mockito-core:1.10.19' 11 | compile 'com.crittercism.dexmaker:dexmaker:1.4' 12 | compile 'com.crittercism.dexmaker:dexmaker-dx:1.4' 13 | compile 'com.crittercism.dexmaker:dexmaker-mockito:1.4' 14 | } 15 | 16 | ext.postBuildExtras = { 17 | android { 18 | compileOptions { 19 | sourceCompatibility JavaVersion.VERSION_1_7 20 | targetCompatibility JavaVersion.VERSION_1_7 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /thali/install/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "install", 3 | "version": "0.0.1", 4 | "description": "Used to handle installing the Thali NPM package correctly into Cordova", 5 | "main": "install.js", 6 | "dependencies": { 7 | "child-process-promise": "^1.1.0", 8 | "bluebird": "3.4.6", 9 | "end-with": "1.0.2", 10 | "findit": "2.0.0", 11 | "randomstring": "1.1.5", 12 | "node-uuid": "1.4.7", 13 | "fs-extra-promise": "0.2.0", 14 | "jxc": "1.0.14", 15 | "lie": "3.1.0", 16 | "request": "2.74.0", 17 | "unzip": "0.1.11", 18 | "xcode": "https://github.com/alunny/node-xcode.git#4cca2f6225c391b63324e6eb53421560649d4f98" 19 | }, 20 | "scripts": { 21 | "setupUnit": "./setUpTests.sh UnitTest_app.js", 22 | "setupPerf": "./setUpTests.sh PerfTest_app.js", 23 | "setupDesktop": "./setUpDesktop.sh" 24 | }, 25 | "author": "Microsoft", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/disabled/testPerfTestFramework.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var PerfTestFramework = require('../../../TestServer/PerfTestFramework.js'); 4 | var TestDevice = require('../../../TestServer/TestDevice.js'); 5 | var tape = require('../lib/thaliTape'); 6 | 7 | var test = tape({ 8 | setup: function (t) { 9 | t.end(); 10 | }, 11 | teardown: function (t) { 12 | t.end(); 13 | } 14 | }); 15 | 16 | test('should be able to add devices to the framework', function (t) { 17 | var testConfig = { 18 | devices: { 19 | ios: 2 20 | }, 21 | honorCount: true 22 | }; 23 | var perfTestFramework = new PerfTestFramework(testConfig); 24 | var testDevice = new TestDevice( 25 | null, 'Some name', 'uuid', 'ios', 'perftest', [], true, null 26 | ); 27 | perfTestFramework.addDevice(testDevice); 28 | t.equal(perfTestFramework.devices.ios.length, 1); 29 | t.end(); 30 | }); 31 | -------------------------------------------------------------------------------- /test/www/jxcore/public/index.css: -------------------------------------------------------------------------------- 1 | .ok { 2 | display:block; 3 | color:grey; 4 | } 5 | 6 | .ok:before { 7 | content: "\2713"; 8 | padding:10px 10px 0 20px; 9 | color:green; 10 | } 11 | 12 | .ok:hover, .fail:hover { 13 | background:#333; 14 | cursor:pointer; 15 | } 16 | 17 | .fail { 18 | display:block; 19 | color:grey; 20 | } 21 | 22 | .fail:before { 23 | content: "\2717"; 24 | padding:10px 10px 0 20px; 25 | color:red; 26 | } 27 | 28 | .msg { 29 | display:block; 30 | margin-top:20px; 31 | margin-bottom:10px; 32 | color:white; 33 | } 34 | 35 | .total-msg { 36 | display:block; 37 | margin-top:20px; 38 | color:white; 39 | } 40 | 41 | .total-fail { 42 | color:red; 43 | } 44 | 45 | .total-ok { 46 | color:green; 47 | } 48 | 49 | body { 50 | background:black; 51 | margin-left:20px; 52 | font-family: source-code-pro, sans-serif; 53 | user-select:none; 54 | font-size:1.2em; 55 | } 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | The information below MUST be provided when filing a bug about a failing test: 2 | 3 | ### URL to the exact commit that you ran the test on. If you haven’t checked in the code that you are testing how is anyone else supposed to figure out what’s going on? 4 | ### The file where the test failed. 5 | ### The test that failed. 6 | ### networkType setting. Did the test fail on wifi, native, both? 7 | ### A link to a gist (or if CI, the GitHub log) with the failure log. 8 | ### Does the test always fail? This is critical. We need to know if the test always fails or sometimes passes. So if a test fails you MUST run it again a few times to be sure. 9 | ### Does the test fail when run on its own? Sometimes we have found that tests fail when run with other tests but don’t fail on their own. So when a test fails you need to run it in isolation (use the .only functionality) to confirm if it behaves the same when run by itself. 10 | -------------------------------------------------------------------------------- /thali/install/utils/child_process.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE.txt file in the project root 3 | // for full license information. 4 | // 5 | 6 | 'use strict'; 7 | 8 | var child_process = require('child_process'); 9 | var Promise = require('./Promise'); 10 | 11 | function exec(command, options) { 12 | return new Promise(function (resolve, reject) { 13 | var childProcess = child_process.exec(command, options); 14 | 15 | childProcess.stdout.on('data', function (data) { 16 | console.log('' + data); 17 | }); 18 | childProcess.stderr.on('data', function (data) { 19 | console.log('' + data); 20 | }); 21 | childProcess.on('close', function (code) { 22 | if (code !== 0) { 23 | return reject('`' + command + '` (exited with error code' + code + ')'); 24 | } 25 | return resolve(); 26 | }); 27 | }); 28 | } 29 | 30 | module.exports.exec = exec; 31 | -------------------------------------------------------------------------------- /thali/install/prePublishThaliCordovaPlugin.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE.txt file in the project root 3 | // for full license information. 4 | // 5 | 6 | 'use strict'; 7 | 8 | var fs = require('fs'); 9 | var path = require('path'); 10 | 11 | // prePublish gets run on 'npm install' 12 | // (e.g. even if you aren't actually publishing) 13 | // so we have to check to make sure that we are in our own directory 14 | // and this isn't some poor user trying to install our package. 15 | 16 | var rootDirectory = path.join(__dirname, '..', '..'); 17 | if (path.basename(rootDirectory) !== 'Thali_CordovaPlugin') { 18 | process.exit(0); 19 | } 20 | 21 | var readMeFileName = 'readme.md'; 22 | var parentReadMe = path.join(__dirname, '..', '..', readMeFileName); 23 | var localReadMe = path.join(__dirname, '..', readMeFileName); 24 | 25 | fs.writeFileSync(localReadMe, fs.readFileSync(parentReadMe)); 26 | process.exit(0); 27 | -------------------------------------------------------------------------------- /test/www/jxcore/views/ejs/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Thali Cordova Plugin Tests 7 | 8 | 9 | 10 |
11 | <% rows.forEach(function (row) { %> 12 | 13 | <% if (row.type === 'test') { %> 14 |
<%= row.name %>
15 | <% } %> 16 | 17 | <% if (row.type === 'assert') { %> 18 | 19 | <% if (row.ok) { %> 20 |
<%= row.name %>
21 | <% } %> 22 | 23 | <% if (!row.ok) { %> 24 |
<%= row.name %>
25 | <% } %> 26 | 27 | <% }); %> 28 | 29 | <% }); %> 30 | 31 |
<%= total %>
32 |
<%= failed %>
33 |
<%= passed %>
34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/TestThaliMobileNative/QuitSignal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | 5 | var logger = require('../../../lib/testLogger')('QuitSignal'); 6 | 7 | 8 | function QuitSignal() { 9 | this.raised = false; 10 | this._handlers = []; 11 | } 12 | 13 | QuitSignal.prototype.bindHandler = function (handler) { 14 | assert( 15 | !this.raised, 16 | 'no calling bindHandler after signal was raised' 17 | ); 18 | this._handlers.push(handler); 19 | }; 20 | 21 | QuitSignal.prototype.unbindHandler = function (handler) { 22 | var index = this._handlers.indexOf(handler); 23 | assert( 24 | index !== -1, 25 | 'handler should exist while unbinding' 26 | ); 27 | this._handlers.splice(index, 1); 28 | }; 29 | 30 | QuitSignal.prototype.raise = function () { 31 | if (this.raised) { 32 | return; 33 | } 34 | this.raised = true; 35 | 36 | this._handlers.forEach(function (handler) { 37 | handler(); 38 | }); 39 | }; 40 | 41 | module.exports = QuitSignal; 42 | -------------------------------------------------------------------------------- /thali/install/include.sh/build-dep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | log_error() { 4 | local filename=$(basename "$0") 5 | local linenumber=${1} 6 | local code="${2:-1}" 7 | 8 | NORMAL_COLOR='\033[0m' 9 | RED_COLOR='\033[0;31m' 10 | 11 | echo "" 12 | echo -e "${RED_COLOR}error: command '${BASH_COMMAND}' failed with code ${code}, file '${filename}' on line ${linenumber}${NORMAL_COLOR}" 13 | } 14 | 15 | running_on_ci() { 16 | # Check the existence of the script that in CI gives the right test server 17 | # IP address. 18 | if [ -x "$(command -v CIGIVEMEMYIP.sh)" ]; then 19 | return 0 20 | else 21 | return 1 22 | fi 23 | } 24 | 25 | get_ci_ip_address() { 26 | if running_on_ci; then 27 | echo "$(CIGIVEMEMYIP.sh)" 28 | fi 29 | } 30 | 31 | is_darwin_platform() { 32 | if test x"`uname`" = xDarwin ; then 33 | return 0 34 | else 35 | return 1 36 | fi 37 | } 38 | 39 | is_minigw_platform() { 40 | if test x"$(uname -s | cut -c 1-5)" == xMINGW ; then 41 | return 0 42 | else 43 | return 1 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /src/android/java/io/jxcore/node/WifiLocker.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import android.net.wifi.WifiManager; 4 | 5 | /** 6 | * Created by evabishchevich on 12/14/16. 7 | */ 8 | 9 | class WifiLocker { 10 | 11 | private static final String LOCK_TAG = "io.jxcore.node.WifiLocker.LOCK_TAG"; 12 | private WifiManager.MulticastLock multicastLock; 13 | 14 | String acquireLock(WifiManager wifiManager) { 15 | String error = null; 16 | try { 17 | if (multicastLock == null) { 18 | multicastLock = wifiManager.createMulticastLock(LOCK_TAG); 19 | multicastLock.setReferenceCounted(false); 20 | } 21 | if (!multicastLock.isHeld()) { 22 | multicastLock.acquire(); 23 | } 24 | } catch (Exception e) { 25 | error = e.getMessage(); 26 | } 27 | return error; 28 | } 29 | 30 | void releaseLock() { 31 | if (multicastLock != null && multicastLock.isHeld()) { 32 | multicastLock.release(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/testUsn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var uuid = require('uuid'); 4 | 5 | var tape = require('../lib/thaliTape'); 6 | var USN = require('thali/NextGeneration/utils/usn'); 7 | 8 | var test = tape({}); 9 | 10 | test('Correctly parses/stringifies USN', function (t) { 11 | var someUuid = uuid.v4(); 12 | var someGeneration = 4; 13 | 14 | var usn = 'data:' + someUuid + ':' + someGeneration; 15 | var invalidPrefix = 'foo:' + someUuid + ':' + someGeneration; 16 | var invalidIdentifier = 'data:a:b:c:0'; 17 | var invalidGeneration = 'data:' + someUuid + ':notanumber'; 18 | 19 | var peer = { 20 | peerIdentifier: someUuid, 21 | generation: someGeneration 22 | }; 23 | 24 | t.deepEqual(USN.parse(usn), peer, 'correctly parses USN string'); 25 | t.throws(function () { 26 | USN.parse(invalidPrefix); 27 | }, 'throws if usn has invalid prefix'); 28 | t.throws(function () { 29 | USN.parse(invalidIdentifier); 30 | }, 'throws if usn has invalid identifier format'); 31 | t.throws(function () { 32 | USN.parse(invalidGeneration); 33 | }, 'throws if usn has invalid generation'); 34 | 35 | t.equal(USN.stringify(peer), usn, 'correctly stringifies peer'); 36 | t.end(); 37 | }); 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | *.DS_Store 3 | 4 | # IntelliJ 5 | .idea/ 6 | .gradle 7 | gradle/ 8 | gradlew 9 | gradlew.bat 10 | *.iml 11 | 12 | # Nodejs 13 | node_modules 14 | npm-debug.log 15 | 16 | # Cordova 17 | out/ 18 | 19 | # VS Code 20 | .settings/ 21 | .vscode/ 22 | 23 | # Testing 24 | test/TestServer/server-address.js 25 | test/www/jxcore/server-address.js 26 | 27 | # Xcode 28 | # 29 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 30 | 31 | ## Build generated 32 | build/ 33 | DerivedData/ 34 | 35 | ## Carthage 36 | lib/ios/Carthage/Build 37 | lib/ios/Carthage/Checkouts 38 | 39 | ## Various settings 40 | *.pbxuser 41 | !default.pbxuser 42 | *.mode1v3 43 | !default.mode1v3 44 | *.mode2v3 45 | !default.mode2v3 46 | *.perspectivev3 47 | !default.perspectivev3 48 | xcuserdata/ 49 | 50 | ## Other 51 | *.moved-aside 52 | *.xcuserstate 53 | 54 | ## Obj-C/Swift specific 55 | *.hmap 56 | *.ipa 57 | *.dSYM.zip 58 | *.dSYM 59 | 60 | ## Playgrounds 61 | timeline.xctimeline 62 | playground.xcworkspace 63 | 64 | # Swift Package Manager 65 | # 66 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 67 | # Packages/ 68 | .build/ 69 | 70 | # Misc 71 | # 72 | 73 | thali/readme.md 74 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/testLogger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Logger = require('thali/ThaliLogger'); 4 | var platform = require('thali/NextGeneration/utils/platform'); 5 | 6 | var logCallback; 7 | var messages = []; 8 | 9 | var isMobile = platform.isMobile; 10 | 11 | if (isMobile) { 12 | Mobile('setLogCallback').registerAsync(function (callback) { 13 | logCallback = callback; 14 | messages.forEach(function (message) { 15 | logCallback(message); 16 | }); 17 | messages = []; 18 | }); 19 | } 20 | 21 | /** 22 | * Log a message to the screen - only applies when running on Mobile. 23 | * It assumes we are using our test framework with our Cordova WebView 24 | * who is setup to receive logging messages and display them. 25 | * @param {object} meta Winston's meta object 26 | * @returns {object} A Winston logger object 27 | */ 28 | module.exports = function (meta) { 29 | var logger = Logger(meta); 30 | if (isMobile) { 31 | logger._thaliLogger.on('message', function (message) { 32 | // 'logCallback' is required. 33 | // We should save all messages before it will be available. 34 | if (logCallback) { 35 | logCallback(message); 36 | } else { 37 | messages.push(message); 38 | } 39 | }); 40 | } 41 | return logger; 42 | }; 43 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testTestUtils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | 6 | var testUtils = require('../lib/testUtils.js'); 7 | var tape = require('../lib/thaliTape'); 8 | 9 | var test = tape({ 10 | setup: function (t) { 11 | t.end(); 12 | }, 13 | teardown: function (t) { 14 | t.end(); 15 | } 16 | }); 17 | 18 | test('should return same temporary folder when called multiple times', 19 | function (t) { 20 | var firstDirectory = testUtils.tmpDirectory(); 21 | var secondDirectory = testUtils.tmpDirectory(); 22 | t.equal(firstDirectory, secondDirectory); 23 | t.end(); 24 | }); 25 | 26 | test('should be able to write to the temporary folder', function (t) { 27 | var temporaryDirectory = testUtils.tmpDirectory(); 28 | console.log(temporaryDirectory); 29 | fs.mkdir(path.join(temporaryDirectory, 'somePath'), function (err) { 30 | t.equal(err, null, 'no error returned when creating a subfolder'); 31 | t.end(); 32 | }); 33 | }); 34 | 35 | test('can call hasRequiredHardware', function (t) { 36 | testUtils.hasRequiredHardware() 37 | .then(function (hasRequiredHardware) { 38 | t.ok(hasRequiredHardware === true || hasRequiredHardware === false, 39 | 'resolves with a boolean'); 40 | t.end(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /thali/validations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var toString = Object.prototype.toString; 4 | 5 | Number.isNaN = Number.isNaN || function(value) { 6 | return toString.call(value) === '[object Number]' && value !== value; 7 | }; 8 | 9 | module.exports.ensureValidPort = function ensureValidPort (n) { 10 | if (toString.call(n) !== '[object Number]' || Number.isNaN(n)) { throw new TypeError('port must be a number'); } 11 | if (n > 65536 || n < 0) { throw new Error('port must be greater than 0 and less than or equal 65536'); } 12 | }; 13 | 14 | module.exports.ensureNonNullOrEmptyString = function ensureNonNullOrEmptyString (value, param) { 15 | if (toString.call(value) !== '[object String]') { throw new TypeError(param + ' must be a string'); } 16 | if (value.trim().length === 0) { throw new Error(param + ' cannot be empty'); } 17 | }; 18 | 19 | module.exports.ensureIsFunction = function ensureIsFunction (value, param) { 20 | if (toString.call(value) !== '[object Function]') { throw new TypeError(param + ' must be a function'); } 21 | }; 22 | 23 | module.exports.objectKeysEquals = function (obj, keys) { 24 | var currentKeys = Object.getOwnPropertyNames(obj); 25 | if (currentKeys.length !== keys.length) { 26 | return false; 27 | } 28 | return keys.every(function (key) { 29 | return obj.hasOwnProperty(key); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/thaliTape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | Thali unit test implementation of tape. 4 | Highly inspired by wrapping-tape. 5 | Usage is very similar to the wrapping tape: 6 | 7 | var tape = require('thaliTape'); 8 | 9 | var test = tape({ 10 | setup: function(t) { 11 | // will be called after each test has started to setup the test 12 | // after the next line, the actual test code will be executed 13 | t.end(); 14 | }, 15 | teardown: function(t) { 16 | // will be called after each device has ended the test 17 | // do any final tear down for the test in here 18 | t.end(); 19 | }, 20 | emitRetryCount: 120, 21 | emitRetryTimeout: 3 * 1000, 22 | setupTimeout: 1 * 60 * 1000, 23 | testTimeout: 10 * 60 * 1000, 24 | teardownTimeout: 1 * 60 * 1000 25 | }); 26 | */ 27 | 28 | require('./utils/process'); 29 | 30 | var exports; 31 | if (typeof jxcore === 'undefined' || typeof Mobile !== 'undefined') { 32 | // On mobile, or outside of jxcore (some dev scenarios) 33 | // we use the server-coordinated thaliTape. 34 | exports = require('./CoordinatedTape'); 35 | exports.coordinated = true; 36 | } else { 37 | // On desktop we just use simple non-coordinated tape. 38 | exports = require('./SimpleTape'); 39 | exports.coordinated = false; 40 | } 41 | exports.sinonTest = require('./sinonTest'); 42 | module.exports = exports; 43 | -------------------------------------------------------------------------------- /thali/install/setUpDesktop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "" 4 | echo "start setUpDesktop.sh" 5 | 6 | SCRIPT_PATH="$(cd "$(dirname "$0")"; pwd -P)" 7 | source "$SCRIPT_PATH/include.sh/build-dep.sh" 8 | 9 | set -euo pipefail 10 | 11 | trap 'log_error $LINENO' ERR 12 | 13 | # These variables are set since a bug in jxcore 14 | # `jx npm install` and `jx install` have different behaviours 15 | NVM_NODEJS_ORG_MIRROR=https://jxcore.azureedge.net 16 | export NVM_NODEJS_ORG_MIRROR 17 | JX_NPM_JXB="${JX_NPM_JXB-jxb311}" 18 | export JX_NPM_JXB 19 | 20 | echo "" 21 | echo "start preparing TestServer" 22 | cd `dirname $0` 23 | cd ../../test/TestServer 24 | npm install --no-optional 25 | node generateServerAddress.js 26 | echo "end preparing TestServer" 27 | echo "" 28 | 29 | echo "" 30 | echo "start installing Thali root" 31 | cd ../../thali 32 | jx npm install --no-optional 33 | npm link 34 | echo "end installing Thali root" 35 | echo "" 36 | 37 | echo "" 38 | echo "start installing Thali install" 39 | cd install 40 | npm install --no-optional 41 | node validateBuildEnvironment.js 42 | echo "end installing Thali install" 43 | echo "" 44 | 45 | echo "" 46 | echo "start preparing 'test/www/jxcore'" 47 | cd ../../test/www/jxcore 48 | npm link thali 49 | node installCustomPouchDB.js 50 | jx npm install --no-optional 51 | echo "end prepating 'test/www/jxcore'" 52 | echo "" 53 | 54 | echo "end setUpDesktop.sh" 55 | echo "" 56 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/IdentityExchange/identityExchangeTestUtils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expressPouchDB = require('express-pouchdb'); 4 | var express = require('express'); 5 | var Promise = require('lie'); 6 | var crypto = require('crypto'); 7 | var identityExchangeUtils = require('thali/identityExchange/identityExchangeUtils'); 8 | var testUtils = require('../lib/testUtils'); 9 | 10 | exports.createThaliAppServer = function () { 11 | var app = express(); 12 | 13 | app.use('/db', expressPouchDB(testUtils.getLevelDownPouchDb(), 14 | { mode: 'minimumForPouchDB'})); 15 | 16 | return new Promise(function (resolve) { 17 | app.listen(0, function () { 18 | this.on('error', function (err) { 19 | console.log('Server got error event: ' + JSON.stringify(err)); 20 | }); 21 | resolve({ app: app, server: this }); 22 | }); 23 | }); 24 | }; 25 | 26 | exports.createSmallAndBigHash = function () { 27 | var random1 = crypto.randomBytes(identityExchangeUtils.pkBufferLength); 28 | var random2 = crypto.randomBytes(identityExchangeUtils.pkBufferLength); 29 | if (identityExchangeUtils.compareEqualSizeBuffers(random1, random2) > 0) { 30 | return { smallHash: random2, bigHash: random1}; 31 | } else { 32 | return { smallHash: random1, bigHash: random2}; 33 | } 34 | }; 35 | 36 | exports.checkCode = function (t, code) { 37 | t.ok(typeof code === 'number' && code >= 0 && code < 1000000, 38 | 'We got a code, did it check out?'); 39 | }; 40 | -------------------------------------------------------------------------------- /thali/install/cordova-hooks/ios/before_plugin_install.js: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt file in the project root 4 | // for full license information. 5 | // 6 | 7 | 'use strict'; 8 | 9 | var path = require('path'); 10 | var fs = require('fs'); 11 | 12 | // Replaces PROJECT_NAME pattern with actual Cordova's project name 13 | function updateJXcoreExtensionImport(context) { 14 | var cordovaUtil = 15 | context.requireCordovaModule('cordova-lib/src/cordova/util'); 16 | var ConfigParser = context.requireCordovaModule('cordova-lib').configparser; 17 | var projectRoot = cordovaUtil.isCordova(); 18 | var xml = cordovaUtil.projectConfig(projectRoot); 19 | var cfg = new ConfigParser(xml); 20 | 21 | var Q = context.requireCordovaModule('q'); 22 | var deferred = new Q.defer(); 23 | 24 | var jxcoreExtensionPath = path.join( 25 | context.opts.plugin.dir, 'src', 'ios', 'JXcoreExtension.m'); 26 | try { 27 | console.log('Updating JXcoreExtension.m'); 28 | 29 | var oldContent = fs.readFileSync(jxcoreExtensionPath, 'utf8'); 30 | var newContent = oldContent.replace('%PROJECT_NAME%', cfg.name()); 31 | fs.writeFileSync(jxcoreExtensionPath, newContent, 'utf8'); 32 | 33 | deferred.resolve(); 34 | } catch (error) { 35 | console.log('Failed updating of JXcoreExtension.m'); 36 | 37 | deferred.reject(error); 38 | } 39 | 40 | return deferred.promise; 41 | } 42 | 43 | module.exports = function (context) { 44 | return updateJXcoreExtensionImport(context); 45 | }; 46 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/IdentityExchange/testIdentityExchangeUtils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tape = require('../lib/thaliTape'); 4 | var identityExchangeUtils = require('thali/identityExchange/identityExchangeUtils'); 5 | 6 | var test = tape({ 7 | setup: function (t) { 8 | t.end(); 9 | }, 10 | teardown: function (t) { 11 | t.end(); 12 | } 13 | }); 14 | 15 | test('test compareEqualSizeBuffers', function (t) { 16 | var testValues = [ 17 | ['', ''], 18 | [null, null], 19 | [new Buffer([0, 0], '')], 20 | ['foo', new Buffer([0, 1])], 21 | [new Buffer([9, 9]), new Buffer([9, 9, 9])] 22 | ]; 23 | testValues.forEach(function (testValue) { 24 | t.throws(function () { 25 | identityExchangeUtils.compareEqualSizeBuffers(testValue[0], testValue[1]); 26 | }); 27 | }); 28 | 29 | var moreTestValues = [ 30 | { result: -1, values: [ 31 | [[0], [1]], 32 | [[0, 1, 1], [1, 1, 1]], 33 | [[7, 7, 6], [7, 7, 7]] 34 | ]}, 35 | { result: 0, values: [ 36 | [[0], [0]], 37 | [[1, 1, 1, 1], [1, 1, 1, 1]] 38 | ]}, 39 | { result: 1, values: [ 40 | [[1], [0]], 41 | [[0, 0, 0, 2], [0, 0, 0, 1]] 42 | ]} 43 | ]; 44 | moreTestValues.forEach(function (testPair) { 45 | testPair.values.forEach(function (value) { 46 | var buffer1 = new Buffer(value[0]); 47 | var buffer2 = new Buffer(value[1]); 48 | var result = identityExchangeUtils.compareEqualSizeBuffers(buffer1, buffer2); 49 | t.equal(testPair.result, result); 50 | }); 51 | }); 52 | t.end(); 53 | }); 54 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testInstall.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var install = require('../../../../thali/install/install.js'); 4 | var tape = require('../lib/thaliTape'); 5 | var testUtils = require('../lib/testUtils.js'); 6 | var exec = require('child_process').exec; 7 | var path = require('path'); 8 | 9 | var test = tape({ 10 | setup: function(t) { 11 | t.end(); 12 | }, 13 | teardown: function(t) { 14 | t.end(); 15 | } 16 | }); 17 | 18 | test('two required plugins should get installed', function (t) { 19 | var tmpDirectory = testUtils.tmpDirectory(); 20 | var testAppName = 'TestApp'; 21 | var testAppDirectory = path.join(tmpDirectory, testAppName); 22 | var cordovaCreateCommand = 'cordova create ' + testAppName; 23 | exec(cordovaCreateCommand, { cwd: tmpDirectory }, function (err, stdout, 24 | stderr) { 25 | if (err) { 26 | t.fail('Cordova command should not fail!'); 27 | t.end(); 28 | return; 29 | } 30 | install(function () { 31 | var cordovaPluginsCommand = 'cordova plugins list'; 32 | exec(cordovaPluginsCommand, { cwd: testAppDirectory }, function (err, 33 | stdout, 34 | stderr) { 35 | t.ok(stdout.indexOf('io.jxcore.node' >= 0), 36 | 'jxcore cordova plugin is installed'); 37 | t.ok(stdout.indexOf('org.thaliproject.p2p' >= 0), 38 | 'thali cordova plugin is installed'); 39 | t.end(); 40 | }); 41 | }, testAppDirectory); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /thali/NextGeneration/utils/platform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _platforms = { 4 | ANDROID: 'android', 5 | IOS: 'ios' 6 | }; 7 | 8 | var _platform = process.platform; 9 | 10 | // All the properties are in the readonly mode 11 | module.exports = Object.defineProperties({}, { 12 | 'name': { 13 | get: function () { 14 | return _platform; 15 | } 16 | }, 17 | 'names' : { 18 | get: function () { 19 | return _platforms; 20 | } 21 | }, 22 | 'isMobile': { 23 | get: function () { 24 | return this.isAndroid || this.isIOS; 25 | } 26 | 27 | }, 28 | 'isAndroid': { 29 | get: function () { 30 | return _platform === _platforms.ANDROID; 31 | } 32 | }, 33 | 'isIOS': { 34 | get: function () { 35 | return _platform === _platforms.IOS; 36 | } 37 | }, 38 | // The methods presented below ONLY for testing reasons 39 | '_override': { 40 | value: function (platform) { 41 | return _platform = platform; 42 | } 43 | }, 44 | '_restore': { 45 | value: function () { 46 | return _platform = process.platform; 47 | } 48 | }, 49 | // Returns REAL values based on `process.platform` 50 | '_realName': { 51 | value: function () { 52 | return process.platform; 53 | } 54 | }, 55 | '_isRealMobile': { 56 | get: function () { 57 | return this._isRealAndroid || this._isRealIOS; 58 | } 59 | 60 | }, 61 | '_isRealAndroid': { 62 | get: function () { 63 | return process.platform === _platforms.ANDROID; 64 | } 65 | }, 66 | '_isRealIOS': { 67 | get: function () { 68 | return process.platform === _platforms.IOS; 69 | } 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /test/TestServer/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Main entry point for Thali test frameworks coordinator server 4 | // jx index.js "{ 'devices': { 'android': 3, 'ios': 2 } }" 5 | 6 | var util = require('util'); 7 | var format = util.format; 8 | 9 | require('./utils/process'); 10 | var logger = require('./utils/ThaliLogger')('TestServer'); 11 | 12 | var HttpServer = require('./HttpServer'); 13 | var UnitTestFramework = require('./UnitTestFramework'); 14 | 15 | var DEFAULT_SERVER_PORT = Number(process.env.COORDINATED_PORT) || 3000; 16 | var WAITING_FOR_DEVICES_TIMEOUT = 5 * 60 * 1000; 17 | 18 | var httpServer = new HttpServer({ 19 | port: DEFAULT_SERVER_PORT, 20 | transports: ['websocket'] 21 | }); 22 | 23 | var options = process.argv[2]; 24 | if (options) { 25 | options = JSON.parse(options); 26 | } 27 | var unitTestManager = new UnitTestFramework(options); 28 | 29 | httpServer 30 | .on('present', function (device) { 31 | switch (device.type) { 32 | case 'unittest': { 33 | unitTestManager.addDevice(device); 34 | break; 35 | } 36 | default: { 37 | throw new Error( 38 | format('unrecognised device type: \'%s\'', device.type) 39 | ); 40 | } 41 | } 42 | }); 43 | 44 | var timer = setTimeout(function () { 45 | throw new Error('timeout exceed'); 46 | }, WAITING_FOR_DEVICES_TIMEOUT); 47 | 48 | unitTestManager 49 | .once('started', function () { 50 | clearTimeout(timer); 51 | }) 52 | .once('completed', function (results) { 53 | logger.debug('completed'); 54 | 55 | var isSuccess = results.every(function (result) { 56 | return result; 57 | }); 58 | if (isSuccess) { 59 | process.exit(0); 60 | } else { 61 | process.exit(1); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testCanBeSkipped.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | 5 | var tape = require('../lib/thaliTape'); 6 | 7 | 8 | var test = tape({ 9 | setup: function (t) { 10 | t.end(); 11 | }, 12 | teardown: function (t) { 13 | t.end(); 14 | } 15 | }); 16 | 17 | var PROMISE_TIMEOUT = 10; 18 | 19 | // These tests should not be skipped. 20 | 21 | test('we should not skip test without \'canBeSkipped\' function', function (t) { 22 | t.pass('passed'); 23 | t.end(); 24 | }); 25 | 26 | test( 27 | 'we should not skip test with \'canBeSkipped\' returned false', 28 | function () { 29 | return false; 30 | }, 31 | function (t) { 32 | t.pass('passed'); 33 | t.end(); 34 | } 35 | ); 36 | 37 | test( 38 | 'we should not skip test with \'canBeSkipped\' returned promise with false', 39 | function () { 40 | return new Promise(function (resolve) { 41 | setTimeout(function () { 42 | resolve(false); 43 | }, PROMISE_TIMEOUT); 44 | }); 45 | }, 46 | function (t) { 47 | t.pass('passed'); 48 | t.end(); 49 | } 50 | ); 51 | 52 | // These tests should be skipped. 53 | 54 | test( 55 | 'we should skip test with \'canBeSkipped\' returned true', 56 | function () { 57 | return true; 58 | }, 59 | function (t) { 60 | t.fail('failed'); 61 | t.end(); 62 | } 63 | ); 64 | 65 | test( 66 | 'we should skip test with \'canBeSkipped\' returned promise with true', 67 | function () { 68 | return new Promise(function (resolve) { 69 | setTimeout(function () { 70 | resolve(true); 71 | }, PROMISE_TIMEOUT); 72 | }); 73 | }, 74 | function (t) { 75 | t.fail('failed'); 76 | t.end(); 77 | } 78 | ); 79 | -------------------------------------------------------------------------------- /test/www/jxcore/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | env : { 5 | BLUEBIRD_DEBUG: false 6 | }, 7 | 8 | // Tests are ordered in the dependency order to avoid testing modules 9 | // before all its dependencies have been tested. 10 | preferredOrder: [ 11 | 'testMakeIntoCloseAllServer.js', 12 | 'testPouchDBCheckpointPlugin.js', 13 | 'testPouchDBGenerator.js', 14 | 'testPromiseQueue.js', 15 | 'testTests.js', 16 | 'testUsn.js', 17 | 'testNativeMethod.js', 18 | 'testThaliMobileNative.js', 19 | 'testThaliMobileNativeAndroid.js', 20 | 'testThaliMobileNativeiOS.js', 21 | 'testThaliMobileNativeDiscoveryCoordinated.js', 22 | 'testCreateNativeListener.js', 23 | 'testCreatePeerListener.js', 24 | 'testThaliTcpServersManager.js', 25 | 'testHttp.js', 26 | 'testThaliMobileNativeWrapper.js', 27 | 'testThaliWifiInfrastructure.js', 28 | 'testThaliMobile.js', 29 | 'testThaliPeerAction.js', 30 | 'testThaliPeerDictionary.js', 31 | 'testThaliPeerPoolDefault.js', 32 | 'testThaliPeerPoolInterface.js', 33 | 'testThaliPeerPoolOneAtATime.js', 34 | 'testThaliNotification.js', 35 | 'testThaliNotificationAction.js', 36 | 'testThaliNotificationBeacons.js', 37 | 'testThaliNotificationClient.js', 38 | 'testThaliNotificationLocal.js', 39 | 'testThaliNotificationServer.js', 40 | 'testThaliPullReplicationFromNotification.js', 41 | 'testThaliPullReplicationFromNotificationCoordinated.js', 42 | 'testThaliReplicationPeerAction.js', 43 | 'testThaliReplicationPeerActionCoordinated.js', 44 | 'testThaliReplicationUtilities.js', 45 | 'testThaliSendNotificationBasedOnReplication.js', 46 | 'testLocalSeqManager.js', 47 | 'testLocalSeqManagerCoordinated.js', 48 | 'testThaliManager.js', 49 | 'testThaliManagerCoordinated.js' 50 | ] 51 | }; 52 | -------------------------------------------------------------------------------- /test/TestServer/IPAddressToFile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs-extra-promise'); 4 | var os = require('os'); 5 | var path = require('path'); 6 | 7 | var Promise = require('./utils/Promise'); 8 | 9 | 10 | function writeFiles(address) { 11 | function writeServerAddress(filePath) { 12 | return fs.writeFileAsync(path.join(filePath, 'server-address.js'), 13 | 'module.exports = "' + address + '";'); 14 | } 15 | 16 | return writeServerAddress(path.join(__dirname, '../www/jxcore')) 17 | .then(function () { 18 | return writeServerAddress(__dirname); 19 | }); 20 | } 21 | 22 | module.exports = function (addressOverride) { 23 | if (addressOverride) { 24 | return writeFiles(addressOverride); 25 | } 26 | 27 | var networkInterfaces = os.networkInterfaces(); 28 | 29 | var ipv4address= null; 30 | Object.keys(networkInterfaces).forEach(function (interfaceName) { 31 | networkInterfaces[interfaceName].forEach(function (iface) { 32 | if ('IPv4' !== iface.family || iface.internal !== false) { 33 | // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses 34 | return; 35 | } 36 | 37 | // We prefer interfaces called Wi-Fi but if we can't find one then we 38 | // will take the first IPv4 address we can find that is not internal. 39 | // The assumption is that the non-Wi-Fi address is connected to a router 40 | // that is connected to Wi-Fi. 41 | if (interfaceName.indexOf('Wi-Fi') > -1){ 42 | // this interface has only one ipv4 address 43 | ipv4address = iface.address; 44 | } 45 | 46 | if (!ipv4address) { 47 | ipv4address = iface.address; 48 | } 49 | }); 50 | }); 51 | 52 | if (!ipv4address) { 53 | return Promise.reject( 54 | new Error('We could not find an IPv4 external facing interface')); 55 | } 56 | 57 | return writeFiles(ipv4address); 58 | }; 59 | -------------------------------------------------------------------------------- /thali/NextGeneration/security/hkdf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | Apache 2.0 License 5 | 6 | Originally from https://github.com/benadida/node-hkdf 7 | Forked in https://github.com/zaach/node-hkdf 8 | 9 | changelog: 10 | 12/10/2015 - applied zaach pull request 11 | 12/10/2015 - removed callback requirement 12 | 12/10/2015 - made instanceof check for easier chaining 13 | */ 14 | 15 | // 16 | // a straightforward implementation of HKDF 17 | // 18 | // https://tools.ietf.org/html/rfc5869 19 | // 20 | 21 | var crypto = require('crypto'); 22 | 23 | function zeros(length) { 24 | var buf = new Buffer(length); 25 | 26 | buf.fill(0); 27 | 28 | return buf.toString(); 29 | } 30 | 31 | // ikm is initial keying material 32 | function HKDF(hashAlg, salt, ikm) { 33 | if (!(this instanceof HKDF)) { return new HKDF(hashAlg, salt, ikm); } 34 | 35 | this.hashAlg = hashAlg; 36 | 37 | // create the hash alg to see if it exists and get its length 38 | var hash = crypto.createHash(this.hashAlg); 39 | this.hashLength = hash.digest().length; 40 | 41 | this.salt = salt || zeros(this.hashLength); 42 | this.ikm = ikm; 43 | 44 | // now we compute the PRK 45 | var hmac = crypto.createHmac(this.hashAlg, this.salt); 46 | hmac.update(this.ikm); 47 | this.prk = hmac.digest(); 48 | } 49 | 50 | HKDF.prototype.derive = function (info, size) { 51 | var prev = new Buffer(0); 52 | 53 | var buffers = []; 54 | var numBlocks = Math.ceil(size / this.hashLength); 55 | 56 | info = new Buffer(info); 57 | 58 | for (var i=0; i < numBlocks; i++) { 59 | var hmac = crypto.createHmac(this.hashAlg, this.prk); 60 | // XXX is there a more optimal way to build up buffers? 61 | var input = Buffer.concat([ 62 | prev, 63 | info, 64 | new Buffer(String.fromCharCode(i + 1)) 65 | ]); 66 | hmac.update(input); 67 | prev = hmac.digest(); 68 | buffers.push(prev); 69 | } 70 | return Buffer.concat(buffers, size); 71 | }; 72 | 73 | module.exports = HKDF; 74 | -------------------------------------------------------------------------------- /thali/NextGeneration/utils/usn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var format = require('util').format; 4 | 5 | /** 6 | * @module utils/usn 7 | * 8 | * Decode/encode peer information from/into USN strings used by SSDP discovery 9 | * mechanism 10 | */ 11 | 12 | /** 13 | * @typdef {Object} UsnPeer 14 | * @property {string} peerIdentifier - UUID part of USN 15 | * @property {number} generation - generation part of USN 16 | */ 17 | 18 | var USN = { 19 | _prefix: 'data:', 20 | }; 21 | 22 | /** 23 | * @param {string} usn 24 | * @returns {UsnPeer} 25 | * @throws Will throw when provided usn has an invalid prefix (other than 26 | * 'data:'), has incorrect number of segments or its generation is not a number. 27 | */ 28 | USN.parse = function (usn) { 29 | if (usn.indexOf(USN._prefix) !== 0) { 30 | throw new Error( 31 | format('Invalid USN (expected "%s" prefix): %s', USN._prefix, usn) 32 | ); 33 | } 34 | var unprefixed = usn.substring(USN._prefix.length); 35 | var segments = unprefixed.split(':'); 36 | var peerIdentifier = segments[0]; 37 | var generation = Number(segments[1]); 38 | 39 | if (segments.length !== 2) { 40 | throw new Error('Invalid USN (expected 2 segments): ' + usn); 41 | } 42 | if (isNaN(generation)) { 43 | throw new Error('Invalid USN (generation is not a number): ' + usn); 44 | } 45 | return { 46 | peerIdentifier: peerIdentifier, 47 | generation: generation 48 | }; 49 | }; 50 | 51 | /** 52 | * @param {UsnPeer} peer 53 | * @returns {string} 54 | */ 55 | USN.stringify = function (peer) { 56 | return USN._prefix + peer.peerIdentifier + ':' + peer.generation; 57 | }; 58 | 59 | /** 60 | * @param {string} usn 61 | * @param {*} errorValue - this value will be returned if usn is invalid 62 | * @returns {UsnPeer|*} 63 | */ 64 | USN.tryParse = function (usn, errorValue) { 65 | try { 66 | return USN.parse(usn); 67 | } catch (error) { 68 | return errorValue; 69 | } 70 | }; 71 | 72 | module.exports = USN; 73 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/MockMobile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var next = {}; 3 | var registered = {}; 4 | 5 | var mocks = { 6 | // Default handlers for mocks. 7 | // If there's an entry in next[key], call that instead 8 | startListeningForAdvertisements : function (cb) { 9 | cb(null); 10 | }, 11 | startUpdateAdvertisingAndListening : function (port, cb) { 12 | cb(null); 13 | } 14 | }; 15 | 16 | /* jshint -W079 */ 17 | var Mobile = function (key) { 18 | /* jshint +W079 */ 19 | return { 20 | // Call a native function 21 | callNative: function () { 22 | // If there's a handler specified then call that 23 | if (key in next) { 24 | var fn = next[key].shift(); 25 | if (next[key].length === 0) { 26 | delete next[key]; 27 | } 28 | fn.apply(this, arguments); 29 | return; 30 | } 31 | 32 | // .. else call the default 33 | if (key in mocks) { 34 | mocks[key].apply(this, arguments); 35 | } 36 | else { 37 | throw new Error('Mock does not implement: ' + key); 38 | } 39 | }, 40 | 41 | // Queue up handlers for native calls 42 | nextNative: function (fn) { 43 | if (key in next) { 44 | next[key].push(fn); 45 | } 46 | else { 47 | next[key] = [fn]; 48 | } 49 | }, 50 | 51 | // Register a function 52 | registerToNative: function (callback) { 53 | registered[key] = callback; 54 | }, 55 | 56 | // Call a registered function 57 | callRegistered: function () { 58 | // Call a registered function 59 | if (key in registered) { 60 | registered[key].apply(this, arguments); 61 | } 62 | else { 63 | throw new Error('Function not registered: ', key); 64 | } 65 | } 66 | }; 67 | }; 68 | 69 | Mobile.createListenerOrIncomingConnection = 70 | function (listeningPort) { 71 | return JSON.stringify({ 72 | listeningPort: listeningPort 73 | }); 74 | }; 75 | 76 | module.exports = Mobile; 77 | -------------------------------------------------------------------------------- /test/TestServer/utils/ThaliLogger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var inherits = util.inherits; 5 | var format = util.format; 6 | 7 | var winston = require('winston'); 8 | var EventEmitter = require('events').EventEmitter; 9 | 10 | 11 | var ThaliLogger = function (logger) { 12 | ThaliLogger.super_.call(this); 13 | 14 | this._logger = logger || ThaliLogger._defaultLogger; 15 | }; 16 | 17 | var defaultLogger; 18 | if ( 19 | typeof jxcore !== 'undefined' && 20 | jxcore.utils && 21 | jxcore.utils.console && 22 | jxcore.utils.console.log 23 | ) { 24 | ThaliLogger._defaultLogger = jxcore.utils.console.log.bind(jxcore.utils.console); 25 | } else { 26 | ThaliLogger._defaultLogger = console.log.bind(console); 27 | } 28 | 29 | util.inherits(ThaliLogger, EventEmitter); 30 | 31 | ThaliLogger.prototype.name = 'ThaliLogger'; 32 | 33 | ThaliLogger.prototype.log = function (level, message, meta, callback) { 34 | var now = new Date().toISOString() 35 | .replace(/T/, ' ') 36 | .replace(/.[^.]+$/, ''); 37 | message = format( 38 | '%s - %s %s: \'%s\'', 39 | now, level.toUpperCase(), meta.tag, message 40 | ); 41 | 42 | this._logger(message); 43 | 44 | // Emit the `logged` event immediately because the event loop 45 | // will not exit until `process.stdout` has drained anyway. 46 | this.emit('logged'); 47 | callback(null, true); 48 | 49 | this.emit('message', message); 50 | }; 51 | 52 | module.exports = function (tag, logger) { 53 | if (!tag || typeof tag !== 'string' || tag.length < 3) { 54 | throw new Error( 55 | 'All logging must have a tag that is at least 3 characters long!' 56 | ); 57 | } 58 | var thaliLogger = new ThaliLogger(logger); 59 | var logger = new winston.Logger({ 60 | transports: [thaliLogger] 61 | }); 62 | logger.rewriters.push(function (level, msg, meta) { 63 | if (!meta.tag) { 64 | meta.tag = tag; 65 | } 66 | return meta; 67 | }); 68 | logger.level = 'debug'; 69 | return logger; 70 | }; 71 | -------------------------------------------------------------------------------- /thali/installCordovaPlugin.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE.txt file in the project root 3 | // for full license information. 4 | // 5 | 6 | 'use strict'; 7 | 8 | var path = require('path'); 9 | var exec = require('./install/utils/child_process').exec; 10 | 11 | // If we are in the test directory inside of the GitHub Repo then we are trying 12 | // to do local development on the desktop and don't need the Cordova 13 | // dependencies 14 | var rootDirectory = path.join(__dirname, '..'); 15 | if (path.basename(rootDirectory) === 'Thali_CordovaPlugin') { 16 | console.log('We believe we are in a clone of the GitHub Repo so we will not '+ 17 | 'install Cordova dependencies'); 18 | process.exit(0); 19 | } 20 | 21 | var installDirectory = path.join(__dirname, 'install'); 22 | 23 | // First check that the installation is done to a Cordova project 24 | exec('cordova info') 25 | .catch(function () { 26 | console.log('The installation directory does not seem to be a Cordova ' + 27 | 'project and currently the installation is supported only to ' + 28 | 'Cordova apps. Please see further information from:'); 29 | console.log('https://github.com/thaliproject/Thali_CordovaPlugin'); 30 | 31 | process.exit(1); 32 | }) 33 | .then(function () { 34 | return exec('npm install --no-optional --production', 35 | { cwd: installDirectory }); 36 | }) 37 | .then(function () { 38 | return exec('find . -name "*.gz" -delete', 39 | { cwd: installDirectory }); 40 | }) 41 | .catch(function (error) { 42 | console.log('Could not install dependencies for install directory. - ' + 43 | error); 44 | process.exit(1); 45 | }) 46 | .then(function () { 47 | require(installDirectory)(function (error) { 48 | if (error) { 49 | console.log('Failed with - ' + error); 50 | process.exit(1); 51 | } 52 | 53 | process.exit(0); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /thali/ThaliLogger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var format = util.format; 5 | 6 | var winston = require('winston'); 7 | 8 | 9 | var ThaliLogger = function (tag) { 10 | ThaliLogger.super_.call(this); 11 | this.tag = tag; 12 | }; 13 | 14 | util.inherits(ThaliLogger, winston.Transport); 15 | 16 | ThaliLogger.prototype.name = 'ThaliLogger'; 17 | 18 | if ( 19 | typeof jxcore !== 'undefined' && 20 | jxcore.utils && 21 | jxcore.utils.console && 22 | jxcore.utils.console.log 23 | ) { 24 | ThaliLogger._logger = jxcore.utils.console.log; 25 | } else { 26 | ThaliLogger._logger = console.log; 27 | } 28 | 29 | // TODO winston is unreliable, we want to find an alternative. 30 | // We can receive last part of message (like errors) in 'meta'. 31 | ThaliLogger.prototype.log = function (level, message, meta, callback) { 32 | if (meta instanceof Error) { 33 | message += ' ' + meta.stack; 34 | } 35 | var now = new Date().toISOString() 36 | .replace(/T/, ' ') 37 | .replace(/.[^.]+$/, ''); 38 | message = format( 39 | '%s - %s %s: \'%s\'', 40 | now, level.toUpperCase(), this.tag, message 41 | ); 42 | 43 | ThaliLogger._logger(message); 44 | 45 | // Emit the `logged` event immediately because the event loop 46 | // will not exit until `process.stdout` has drained anyway. 47 | this.emit('logged'); 48 | callback(null, true); 49 | }; 50 | 51 | module.exports = function (tag) { 52 | if (!tag || typeof tag !== 'string' || tag.length < 3) { 53 | throw new Error( 54 | 'All logging must have a tag that is at least 3 characters long!' 55 | ); 56 | } 57 | var thaliLogger = new ThaliLogger(tag); 58 | var logger = new winston.Logger({ 59 | transports: [thaliLogger] 60 | }); 61 | logger._thaliLogger = thaliLogger; 62 | logger.level = 'debug'; 63 | 64 | // Node-SSDP uses Bunyan which supports trace, Winston does not. To work 65 | // around this we are hacking in trace support. 66 | logger.trace = logger.silly; 67 | return logger; 68 | }; 69 | -------------------------------------------------------------------------------- /test/www/jxcore/perf_tests/disabled/testNewFindPeers.js: -------------------------------------------------------------------------------- 1 | var ThaliEmitter = require('thali/thaliemitter'); 2 | 3 | function testNewFindPeers(testData, name, peerCount, bluetoothAddresses) { 4 | this.deviceName = name; 5 | this.testData = testData; 6 | this.peerCount = peerCount; 7 | this.bluetoothAddresses = bluetoothAddresses; 8 | } 9 | 10 | testNewFindPeers.prototype.start = function() { 11 | 12 | var self = this; 13 | 14 | this.startTime = new Date(); 15 | this.emitter = new ThaliEmitter(); 16 | 17 | var discoveredPeers = {}; 18 | this.emitter.on(ThaliEmitter.events.PEER_AVAILABILITY_CHANGED, function(peers) { 19 | var now = new Date(); 20 | peers.forEach(function(peer) { 21 | if (!(peer.peerIdentifier in discoveredPeers) && peer.peerAvailable) { 22 | discoveredPeers[peer.peerIdentifier] = now - startTime; 23 | if (Object.keys(discoveredPeers).length == self.testData.peerCount) { 24 | self.reportResults(discoveredPeers); 25 | self.stop(); 26 | } 27 | } 28 | }); 29 | }); 30 | 31 | this.emitter.startBroadcasting(this.deviceName, 4242, function (err) { 32 | if (err) { 33 | self.reportResults([], err); 34 | self.stop(); 35 | } 36 | ); 37 | } 38 | 39 | testNewFindPeers.prototype.stop = function() { 40 | this.emitter.stopBroadcasting(function(err) { 41 | console.log(err); 42 | this.emitter = null; 43 | }; 44 | } 45 | 46 | testNewFindPeers.prototype.reportResults = function(discoveredPeers, err) { 47 | 48 | var results = []; 49 | 50 | for (var peer in discoveredPeers) { 51 | results.push({ 52 | "peerName": peer.peerName, 53 | "peerIdentifier": peer.peerIdentifier, 54 | "peerAvailable": peer.peerAvailable, 55 | "time": discoveredPeers[peer] 56 | }); 57 | } 58 | 59 | this.emit('done', JSON.stringify({ 60 | "name:": this.deviceName, 61 | "time": new Date() - this.startTime, 62 | "result": "OK", 63 | "peersList": results 64 | })); 65 | } 66 | 67 | module.exports = testNewFindPeers; 68 | -------------------------------------------------------------------------------- /test/www/jxcore/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thali-cordova-plugin-jxcore", 3 | "version": "1.0.0", 4 | "description": "Thali Cordova Plugin JXCore", 5 | "main": "runTests.js", 6 | "scripts": { 7 | "test": "jx runTests.js", 8 | "test-meta": "jx runTests.js meta_tests", 9 | "test-coordinated": "jx runCoordinatedTests.js" 10 | }, 11 | "keywords": [ 12 | "Thali", 13 | "JXCore", 14 | "PouchDB", 15 | "Multiplex" 16 | ], 17 | "author": "Microsoft Corporation", 18 | "license": "MIT", 19 | "dependencies": { 20 | "balanced-match": "0.2.0", 21 | "bluebird": "3.4.6", 22 | "bn.js": "4.10.0", 23 | "body-parser": "1.13.3", 24 | "child-process-promise": "1.1.0", 25 | "concat-map": "0.0.1", 26 | "express": "4.13.3", 27 | "express-pouchdb": "1.0.6-thali", 28 | "forever-agent": "https://github.com/thaliproject/forever-agent.git#f9a92c7a1b7ce4da849beca32b0ec2aeb855e0fd", 29 | "fs-extra-promise": "0.4.0", 30 | "inherits": "2.0.1", 31 | "is-property": "1.0.2", 32 | "js-extend": "1.0.1", 33 | "leveldown-mobile": "1.1.1", 34 | "lie": "3.0.4", 35 | "long": "3.0.3", 36 | "minimist": "1.2.0", 37 | "multiplex": "6.7.0", 38 | "nock": "2.12.0", 39 | "node-ssdp": "https://github.com/thaliproject/node-ssdp.git#3a5909e201aee401f48965e378eb2ff0e2f9e027", 40 | "node-uuid": "1.4.7", 41 | "object-assign": "4.1.0", 42 | "pouchdb": "6.1.1", 43 | "pouchdb-size": "1.2.2", 44 | "proxyquire": "1.7.4", 45 | "randomstring": "1.1.5", 46 | "request": "2.64.0", 47 | "request-promise": "0.4.3", 48 | "salti": "https://github.com/thaliproject/salti.git#master", 49 | "sinon": "1.17.3", 50 | "socket.io-client": "1.4.8", 51 | "stacky": "1.3.1", 52 | "supertest": "1.1.0", 53 | "supertest-as-promised": "2.0.2", 54 | "tape": "4.0.0", 55 | "tape-catch": "1.0.4", 56 | "thali": "*", 57 | "tmp": "0.0.28", 58 | "urlsafe-base64": "1.0.0", 59 | "uuid": "2.0.0", 60 | "uuid-validate": "0.0.2" 61 | }, 62 | "devDependencies": {} 63 | } 64 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/disabled/testPerfTestFrameworkClient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var originalMobile = typeof Mobile === 'undefined' ? undefined : Mobile; 4 | var mockMobile = require('../bv_tests/disabled/mockmobile.js'); 5 | var PerfTestFrameworkClient = require('../perf_tests/PerfTestFrameworkClient.js'); 6 | var tape = require('../lib/thaliTape'); 7 | var EventEmitter = require("events").EventEmitter; 8 | 9 | var test = tape({ 10 | setup: function(t) { 11 | global.Mobile = mockMobile; 12 | t.end(); 13 | }, 14 | teardown: function(t) { 15 | global.Mobile = originalMobile; 16 | t.end(); 17 | } 18 | }); 19 | 20 | test('passing wrong test name should throw', function (t) { 21 | 22 | var mockServer = new EventEmitter(); 23 | var perfTestFrameworkClient = new PerfTestFrameworkClient('Some device name', null, mockServer); 24 | 25 | mockServer.on("error", function(msg) { 26 | t.ok(msg != null, msg); 27 | t.end(); 28 | }); 29 | 30 | var testData = { 31 | 'testName': 'testThatDoesNotExist', 32 | 'addressList': [] 33 | }; 34 | 35 | mockServer.emit("start", testData); 36 | }); 37 | 38 | test('own address should be filtered out and try count reseted', function (t) { 39 | var myAddress = 'C0:EE:EE:EE:42:00' 40 | var dummyAddress = 'C0:FF:FF:EE:42:00'; 41 | 42 | var mockServer = new EventEmitter(); 43 | var perfTestFrameworkClient = new PerfTestFrameworkClient('Some device name', myAddress, mockServer); 44 | 45 | perfTestFrameworkClient.tests = { 46 | 'mockTest': function (testData, deviceName, addressList) { 47 | t.ok(addressList.length === 1, 'own address filtered out'); 48 | t.ok(addressList[0].address === dummyAddress, 'other addresses in the address property'); 49 | t.ok(addressList[0].tryCount === 0, 'try count is 0 in the beginning'); 50 | return { 51 | start: function () { 52 | t.end(); 53 | }, 54 | on: function () {} 55 | }; 56 | } 57 | }; 58 | 59 | var testData = { 60 | 'testName': 'mockTest', 61 | 'addressList': [myAddress, dummyAddress] 62 | }; 63 | 64 | mockServer.emit('start', testData); 65 | }); 66 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/testLoader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs-extra-promise'); 4 | var path = require('path'); 5 | var format = require('util').format; 6 | 7 | var logger = require('./testLogger')('testLoader'); 8 | 9 | 10 | function sortFiles(files, preferredOrder) { 11 | if (!Array.isArray(preferredOrder)) { 12 | logger.info('Tests order is not specified. They will be sorted by name'); 13 | preferredOrder = []; 14 | } 15 | files.sort(function (f1, f2) { 16 | var f1Name = path.basename(f1); 17 | var f2Name = path.basename(f2); 18 | var index1 = preferredOrder.indexOf(f1Name); 19 | var index2 = preferredOrder.indexOf(f2Name); 20 | 21 | if (index1 === -1 && index2 === -1) { 22 | // compare by name 23 | return f1Name > f2Name ? 1 : f1Name === f2Name ? 0 : -1; 24 | } 25 | 26 | // move unknown tests to the end of the list 27 | if (index1 === -1) { return 1; } 28 | if (index2 === -1) { return -1; } 29 | 30 | return index1 - index2; 31 | }); 32 | } 33 | 34 | 35 | function hasJavaScriptSuffix (path) { 36 | return path.indexOf('.js', path.length - 3) !== -1; 37 | }; 38 | 39 | function loadFile (filePath) { 40 | logger.info('loading file:', filePath); 41 | try { 42 | require(filePath); 43 | } catch (error) { 44 | var prettyError = format( 45 | 'test load failed, filePath: \'%s\', error: \'%s\', stack: \'%s\'', 46 | filePath, error.toString(), error.stack 47 | ); 48 | logger.error(prettyError); 49 | throw new Error(prettyError); 50 | } 51 | }; 52 | 53 | module.exports.load = function (testsToRun, preferredOrder) { 54 | if (hasJavaScriptSuffix(testsToRun)) { 55 | loadFile(testsToRun); 56 | } else { 57 | var testFiles = fs.readdirSync(testsToRun).filter(function (fileName) { 58 | return fileName.indexOf('test') === 0 && hasJavaScriptSuffix(fileName); 59 | }); 60 | sortFiles(testFiles, preferredOrder); 61 | testFiles.forEach(function (fileName) { 62 | if (fileName.indexOf('test') === 0 && hasJavaScriptSuffix(fileName)) { 63 | var filePath = path.join(testsToRun, fileName); 64 | loadFile(filePath); 65 | } 66 | }); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testPouchDBAgent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var objectAssign = require('object-assign'); 4 | var ForeverAgent = require('forever-agent'); 5 | var express = require('express'); 6 | var expressPouchDB = require('express-pouchdb'); 7 | var http = require('http'); 8 | 9 | var tape = require('../lib/thaliTape'); 10 | var testUtils = require('../lib/testUtils'); 11 | 12 | var PouchDB = testUtils.getLevelDownPouchDb(); 13 | 14 | 15 | var test = tape({ 16 | setup: function (t) { 17 | t.end(); 18 | }, 19 | teardown: function (t) { 20 | t.end(); 21 | } 22 | }); 23 | 24 | test('PouchDB agent works as expected', function (t) { 25 | var agent = new ForeverAgent(); 26 | 27 | var requestsData = []; 28 | var _addRequest = agent.addRequest; 29 | agent.addRequest = function (data) { 30 | requestsData.push(objectAssign({}, data)); 31 | return _addRequest.apply(this, arguments); 32 | }; 33 | 34 | var app = express(); 35 | app.use('/db', expressPouchDB(PouchDB)); 36 | 37 | var server = http.createServer(app); 38 | server.listen(0, function () { 39 | var port = server.address().port; 40 | var url = 'http://localhost:' + port + '/db'; 41 | var db = new PouchDB(url, { 42 | ajax: { 43 | agent: agent 44 | } 45 | }); 46 | 47 | function query(name) { 48 | return db.get(name) 49 | .then(function () { 50 | t.fail('we should not find a document'); 51 | }) 52 | .catch(function (error) { 53 | t.ok(error, 'error is not empty'); 54 | t.equals(error.status, 404, 'status should be 404'); 55 | }) 56 | .then(function () { 57 | var lastData = requestsData[requestsData.length - 1]; 58 | t.equals(lastData.method, 'GET', 'method is \'get\''); 59 | t.equals(lastData.path, '/db/' + name + '?', 'path is ok'); 60 | }); 61 | } 62 | 63 | query('fit') 64 | .then(function () { 65 | return query('foo'); 66 | }) 67 | .then(function () { 68 | t.ok(requestsData.length > 2, 'we should call agent more than twice'); 69 | t.end(); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testResultsProcessor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ResultsProcessor = require('../../../TestServer/ResultsProcessor.js'); 4 | var tape = require('../lib/thaliTape'); 5 | 6 | var test = tape({ 7 | setup: function (t) { 8 | t.end(); 9 | }, 10 | teardown: function (t) { 11 | t.end(); 12 | } 13 | }); 14 | 15 | test('should be able to process valid results without exceptions', function (t) { 16 | var testResults = [ 17 | { 18 | 'data': { 19 | 'name:': 'LGE-Nexus 5_PT7062', 20 | 'result': 'OK', 21 | 'sendList': [ 22 | { 23 | 'connections': 3, 24 | 'dataAmount': 100000, 25 | 'dataReceived': 100000, 26 | 'doneRounds': 1, 27 | 'name': '90:E7:C4:FC:13:3C', 28 | 'result': 'OK', 29 | 'time': 57792, 30 | 'tryCount': 1 31 | } 32 | ], 33 | 'time': 119819 34 | }, 35 | 'device': 'LGE-Nexus 5_PT7062', 36 | 'test': 0, 37 | 'time': 120073 38 | }, 39 | { 40 | 'data': { 41 | 'name:': 'HTC-HTC6535LVW_PT3841', 42 | 'result': 'OK', 43 | 'sendList': [ 44 | { 45 | 'connections': 2, 46 | 'dataAmount': 100000, 47 | 'dataReceived': 100000, 48 | 'doneRounds': 5, 49 | 'name': 'F8:95:C7:13:51:1E', 50 | 'result': 'OK', 51 | 'time': 30550, 52 | 'tryCount': 1 53 | } 54 | ], 55 | 'time': 115137 56 | }, 57 | 'device': 'HTC-HTC6535LVW_PT3841', 58 | 'test': 0, 59 | 'time': 115231 60 | }, 61 | { 62 | 'data': { 63 | 'name:': 'A5-1', 64 | 'time': 10005, 65 | 'result': 'TIMEOUT', 66 | 'sendList': [] 67 | }, 68 | 'device': 'A5-1', 69 | 'test': 0, 70 | 'time': 100051 71 | } 72 | ]; 73 | var testDevices = { 74 | 'LGE-Nexus 5_PT7062': {}, 75 | 'HTC-HTC6535LVW_PT3841': {}, 76 | 'A5-1': {} 77 | }; 78 | var processedResults = ResultsProcessor.process(testResults, testDevices); 79 | t.ok(processedResults, 'received processed results'); 80 | t.end(); 81 | }); 82 | -------------------------------------------------------------------------------- /thali/NextGeneration/identityExchange/connectionTable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var EventEmitter = require('events').EventEmitter; 4 | var inherits = require('util').inherits; 5 | var ThaliReplicationManager = require('../thalireplicationmanager'); 6 | 7 | inherits(ConnectionTable, EventEmitter); 8 | 9 | ConnectionTable.prototype.thaliReplicationManager = null; 10 | ConnectionTable.prototype.connectionTable = {}; 11 | ConnectionTable.prototype.connectionSuccessListener = null; 12 | ConnectionTable.prototype.cleanUpWasCalled = false; 13 | ConnectionTable.prototype.cleanUpCalledErrorMessage = 14 | 'Cleanup was called, this table is no longer live.'; 15 | 16 | ConnectionTable.prototype.lookUpPeerId = function (peerId, lastTime) { 17 | if (this.cleanUpWasCalled) { 18 | throw new Error(this.cleanUpCalledErrorMessage); 19 | } 20 | 21 | var tableEntry = this.connectionTable[peerId]; 22 | 23 | tableEntry = tableEntry === undefined ? null : tableEntry; 24 | 25 | if (!tableEntry || !lastTime) { 26 | return tableEntry; 27 | } 28 | 29 | return tableEntry.time > lastTime ? tableEntry : null; 30 | }; 31 | 32 | ConnectionTable.prototype.cleanUp = function () { 33 | this.cleanUpWasCalled = true; 34 | this.thaliReplicationManager.removeListener(ThaliReplicationManager.events.CONNECTION_SUCCESS, 35 | this.connectionSuccessListener); 36 | }; 37 | 38 | /** 39 | * A temporary hack to collect connectionSuccess events. Once we put in ACLs we won't need this hack anymore. 40 | * @param thaliReplicationManager 41 | * @constructor 42 | */ 43 | function ConnectionTable(thaliReplicationManager) { 44 | EventEmitter.call(this); 45 | var self = this; 46 | this.thaliReplicationManager = thaliReplicationManager; 47 | this.connectionTable = {}; 48 | this.connectionSuccessListener = function (successObject) { 49 | self.connectionTable[successObject.peerIdentifier] = { 50 | muxPort: successObject.muxPort, 51 | time: Date.now() 52 | }; 53 | 54 | self.emit(successObject.peerIdentifier, self.connectionTable[successObject.peerIdentifier]); 55 | }; 56 | thaliReplicationManager.on(ThaliReplicationManager.events.CONNECTION_SUCCESS, this.connectionSuccessListener); 57 | } 58 | 59 | module.exports = ConnectionTable; 60 | 61 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/mockmobile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var keys = {}; 4 | 5 | // arguments needed to simulate the GetDocumentsPath() function 6 | var errArgGetDocumentsPath; 7 | var fileLocationArgGetDocumentsPath; 8 | 9 | function Mobile(key) { 10 | keys.hasOwnProperty(key) || (keys[key] = new Mobile.NativeCall(key)); 11 | return keys[key]; 12 | } 13 | 14 | Mobile.iAmAMock = true; 15 | 16 | Mobile.setGetDocumentsPathReturnValues = function (errArg, fileLocationArg) { 17 | errArgGetDocumentsPath = errArg; 18 | fileLocationArgGetDocumentsPath = fileLocationArg; 19 | }; 20 | 21 | Mobile.GetDocumentsPath = function (cb) { 22 | cb(errArgGetDocumentsPath, fileLocationArgGetDocumentsPath); 23 | }; 24 | 25 | Mobile.invokeNative = function (key, arg) { 26 | keys[key].registerNativeCallback(arg); 27 | }; 28 | 29 | function invokeCallback(key, args) { 30 | var localArgs = keys[key].callNativeArguments; 31 | var cb = localArgs[localArgs.length - 1]; 32 | cb.apply(null, args); 33 | } 34 | 35 | Mobile.invokeConnect = function () { 36 | var len = arguments.length, args = new Array(len); 37 | for (var i = 0; i < len; i++) { args[i] = arguments[i]; } 38 | invokeCallback('Connect', args); 39 | }; 40 | 41 | Mobile.invokeDisconnect = function () { 42 | var len = arguments.length, args = new Array(len); 43 | for (var i = 0; i < len; i++) { args[i] = arguments[i]; } 44 | invokeCallback('Disconnect', args); 45 | }; 46 | 47 | Mobile.invokeStartBroadcasting = function () { 48 | var len = arguments.length, args = new Array(len); 49 | for (var i = 0; i < len; i++) { args[i] = arguments[i]; } 50 | invokeCallback('StartBroadcasting', args); 51 | }; 52 | 53 | Mobile.invokeStopBroadcasting = function () { 54 | var len = arguments.length, args = new Array(len); 55 | for (var i = 0; i < len; i++) { args[i] = arguments[i]; } 56 | invokeCallback('StopBroadcasting', args); 57 | }; 58 | 59 | Mobile.NativeCall = function (key) { 60 | this.key = key; 61 | this.registerNativeCallback = null; 62 | this.callNativeArguments = null; 63 | }; 64 | 65 | Mobile.NativeCall.prototype = { 66 | registerToNative: function (cb) { 67 | this.registerNativeCallback = cb; 68 | }, 69 | callNative: function () { 70 | this.callNativeArguments = arguments; 71 | } 72 | }; 73 | 74 | module.exports = Mobile; 75 | -------------------------------------------------------------------------------- /test/www/jxcore/readme.md: -------------------------------------------------------------------------------- 1 | # Thali Cordova Plugin Replication Manager Tests # 2 | 3 | ## Overview 4 | 5 | ### Directory structure 6 | 7 | ``` 8 | |____bv_tests/ <- Build Verification tests. You should run these often. 9 | |____lib/ <- Support files for running tests. 10 | |____perf_tests/ <- Performance tests. Long running tests. For nightly builds etc. 11 | |____meta_tests/ <- Tests non-production code like test frameworks and installation. 12 | |____readme.md <- This file. 13 | |____runTests.js <- The test runner to run tests stand-alone. 14 | |____runCoordinatedTests.js <- The test runner to run tests with test coordination server. 15 | |____server-address.js <- Contains the IP address of the test coordination server. 16 | |____PerfTest_app.js <- Rename to app.js, build and deploy to run perf tests. 17 | |____UnitTest_app.js <- Rename to app.js, build and deploy to run bv tests. 18 | ``` 19 | 20 | ## Running the tests 21 | 22 | ## Contributing 23 | 24 | If you see a mistake, find a bug, or you think there is a better way to do something, feel free to contribute. 25 | Please see our [contribution page](http://thaliproject.org/WaysToContribute) for ways to connect and start 26 | contributing to Thali. 27 | 28 | ## License 29 | 30 | Copyright (c) 2015 Microsoft 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 33 | 34 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | -------------------------------------------------------------------------------- /src/android/java/io/jxcore/node/SurroundingStateObserver.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import org.thaliproject.p2p.btconnectorlib.PeerProperties; 4 | 5 | /** 6 | * Created by evabishchevich on 1/3/17. 7 | */ 8 | 9 | public interface SurroundingStateObserver { 10 | 11 | /** 12 | * Notifies node layer about peer changes detected on native Android layer 13 | * 14 | * @param peerProperties Peer properties of new/updated/lost peer. 15 | * @param isAvailable If true, peer is available. False otherwise. 16 | */ 17 | void notifyPeerAvailabilityChanged(PeerProperties peerProperties, boolean isAvailable); 18 | 19 | /** 20 | * Notifies about discovery and/or advertising changes 21 | * 22 | * @param isDiscoveryActive We are currently discovering if true. False otherwise. 23 | * @param isAdvertisingActive We are currently advertising if true. False otherwise. 24 | */ 25 | void notifyDiscoveryAdvertisingStateUpdateNonTcp(boolean isDiscoveryActive, boolean isAdvertisingActive); 26 | 27 | /** 28 | * @param isBluetoothEnabled If true, Bluetooth is enabled. False otherwise. 29 | * @param isWifiEnabled If true, Wi-Fi is enabled. False otherwise. 30 | * @param bssidName If null this value indicates that either wifiRadioOn is not 'on' or 31 | * that the Wi-Fi isn't currently connected to an access point. 32 | * If non-null then this is the BSSID of the access point that Wi-Fi 33 | * is connected to. 34 | * @param ssidName If null this value indicates that either wifiRadioOn is not 'on' or 35 | * that the Wi-Fi isn't currently connected to an access point. 36 | * If non-null then this is the SSID of the access point that Wi-Fi 37 | * is connected to. 38 | */ 39 | void notifyNetworkChanged(boolean isBluetoothEnabled, boolean isWifiEnabled, String bssidName, String ssidName); 40 | 41 | /** 42 | * This event is guaranteed to be not sent more often than every 100 ms. 43 | * 44 | * @param portNumber The 127.0.0.1 port that the TCP/IP bridge tried to connect to. 45 | */ 46 | void notifyIncomingConnectionToPortNumberFailed(int portNumber); 47 | } 48 | -------------------------------------------------------------------------------- /test/www/jxcore/perf_tests/ReConnectTCPServer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * This test is needing all three files to be present 4 | * - testReConnect.js : the main entry point to the test case 5 | * - ReConnectConnector.js : logic that handles the connection & data sending parts 6 | * - ReConnectTCPServer.js : logic that handles the server endpoint for connections & data receiving/replying for the test 7 | * 8 | * In this test case we try connecting to the remote peer and verify that the connection works by sending small amount of data (that gets echoed back) 9 | * We measure the time it takes to create the connection, and then disconnect and do re-connections as specified by the test data 10 | */ 11 | 'use strict'; 12 | var net = require('net'); 13 | 14 | 15 | function ReConnectTCPServer(port) { 16 | var self = this; 17 | self.port = port; 18 | 19 | this.stopServer(); 20 | this.server = net.createServer(function (c) { //'connection' listener 21 | console.log('TCP/IP server connected'); 22 | 23 | c.on('end', function () { 24 | console.log('TCP/IP server is ended'); 25 | }); 26 | c.on('close', function () { 27 | console.log('TCP/IP server is close'); 28 | }); 29 | c.on('error', function (err) { 30 | console.log('TCP/IP server got error : ' + err); 31 | }); 32 | 33 | c.on('data', function (data) { 34 | console.log("TCP/IP server got data - " + data.length); 35 | c.write(data.toString()); 36 | }); 37 | }); 38 | 39 | this.server.on('error', function (data) { 40 | console.log("TCP/IP server error: " + data.toString()); 41 | }); 42 | this.server.on('close', function () { 43 | console.log('TCP/IP server socket is disconnected'); 44 | }); 45 | 46 | this.server.listen(port, function() { //'listening' listener 47 | console.log('TCP/IP server is bound to : ' + this.port); 48 | }); 49 | } 50 | ReConnectTCPServer.prototype.getServerPort = function() { 51 | return (this.server && this.server.address()) ? this.server.address().port : 0; 52 | } 53 | 54 | ReConnectTCPServer.prototype.stopServer = function() { 55 | if(this.server == null) { 56 | return; 57 | } 58 | 59 | this.server.close(); 60 | this.server = null; 61 | } 62 | 63 | module.exports = ReConnectTCPServer; 64 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/testNativeMethod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var platform = require('thali/NextGeneration/utils/platform'); 4 | 5 | var tape = require('../lib/thaliTape'); 6 | var thaliMobileNativeWrapper = require('../node_modules/thali/NextGeneration/thaliMobileNativeWrapper'); 7 | 8 | var callbackPeer; 9 | 10 | var test = tape({ 11 | setup: function (t) { 12 | t.end(); 13 | }, 14 | teardown: function (t) { 15 | thaliMobileNativeWrapper._registerToNative(); 16 | t.end(); 17 | } 18 | }); 19 | 20 | test('onPeerLost calls jxcore', 21 | function () { 22 | return !platform._isRealAndroid; 23 | }, 24 | function (t) { 25 | Mobile('peerAvailabilityChanged').registerToNative(function (peers) { 26 | if (!Array.isArray(peers)) { 27 | peers = [peers]; 28 | t.fail('peers callback should be an array!'); 29 | } 30 | 31 | t.equals(peers.length, 1, 'There should be exactly one peer'); 32 | callbackPeer = peers[0]; 33 | 34 | t.equal(callbackPeer.peerIdentifier, '11:22:33:22:11:00', 35 | 'check if callback was fired by onPeerLost'); 36 | t.ok(callbackPeer.generation === null, 'check if generation is null'); 37 | t.notOk(callbackPeer.peerAvailable, 'check if peerAvailable is false'); 38 | 39 | t.end(); 40 | }); 41 | 42 | Mobile('testNativeMethod').callNative('onPeerLost', function (result) { 43 | t.pass(result.Testing_); 44 | }); 45 | }); 46 | 47 | test('onPeerDiscovered calls jxcore', 48 | function () { 49 | return !platform._isRealAndroid; 50 | }, 51 | function (t) { 52 | Mobile('peerAvailabilityChanged').registerToNative(function (peers) { 53 | 54 | if (!Array.isArray(peers)) { 55 | peers = [peers]; 56 | t.fail('peers callback should be an array!'); 57 | } 58 | 59 | t.equals(peers.length, 1, 'There should be exactly one peer'); 60 | callbackPeer = peers[0]; 61 | 62 | t.equal(callbackPeer.peerIdentifier, '33:44:55:44:33:22', 63 | 'check if callback was fired by onPeerDiscovered'); 64 | t.equal(callbackPeer.generation, 0, 'check if generation is 0'); 65 | t.ok(callbackPeer.peerAvailable, 'check if peerAvailable is true'); 66 | 67 | t.end(); 68 | }); 69 | 70 | Mobile('testNativeMethod').callNative('onPeerDiscovered', function (result) { 71 | t.pass(result.Testing_); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/www/jxcore/perf_tests/SendDataTCPServer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | 5 | var logger = function (value) { 6 | //console.log(new Date().toJSON() + ' SendDataTCPServer.js: ' + value); 7 | }; 8 | 9 | function SendDataTCPServer(port) { 10 | var self = this; 11 | self.port = port; 12 | 13 | var limitToReport = 10000; 14 | 15 | this.server = net.createServer(function (c) { //'connection' listener 16 | var receivedDataInBytes = 0; 17 | var lastReportedAmount = 0; 18 | 19 | logger('TCP/IP server connected'); 20 | 21 | c.on('end', function () { 22 | logger('TCP/IP server is ended'); 23 | c.destroy(); 24 | }); 25 | 26 | c.on('error', function (err) { 27 | logger('TCP/IP server got error : ' + err); 28 | }); 29 | 30 | c.on('data', function (data) { 31 | receivedDataInBytes += data.length; 32 | logger('TCP/IP server has received ' + receivedDataInBytes + ' bytes of data'); 33 | var dataSincePreviousReport = receivedDataInBytes - lastReportedAmount; 34 | 35 | if (dataSincePreviousReport >= limitToReport) { 36 | var acknowledgmentCount = Math.round(dataSincePreviousReport / limitToReport); 37 | 38 | for (var i = 0; i < acknowledgmentCount; i++) { 39 | c.write('ACK'); 40 | } 41 | 42 | lastReportedAmount += acknowledgmentCount * limitToReport; 43 | } 44 | }); 45 | }); 46 | 47 | this.server.on('error', function (data) { 48 | logger("TCP/IP server error: " + data.toString()); 49 | }); 50 | this.server.on('close', function () { 51 | logger('TCP/IP server socket is disconnected'); 52 | }); 53 | 54 | this.server.listen(port, function () { 55 | logger('TCP/IP server is bound to port: ' + self.getServerPort()); 56 | }); 57 | } 58 | SendDataTCPServer.prototype.getServerPort = function() { 59 | return (this.server && this.server.address()) ? this.server.address().port : 0; 60 | } 61 | 62 | SendDataTCPServer.prototype.stopServer = function (callback) { 63 | if (this.server == null) { 64 | if (callback) callback(); 65 | return; 66 | } 67 | this.server.close(callback); 68 | this.server = null; 69 | } 70 | 71 | module.exports = SendDataTCPServer; 72 | -------------------------------------------------------------------------------- /thali/install/ios/build-ci-no-tests.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one 3 | // or more contributor license agreements. See the NOTICE file 4 | // distributed with this work for additional information 5 | // regarding copyright ownership. The ASF licenses this file 6 | // to you under the Apache License, Version 2.0 (the 7 | // "License"); you may not use this file except in compliance 8 | // with the License. You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, 13 | // software distributed under the License is distributed on an 14 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | // KIND, either express or implied. See the License for the 16 | // specific language governing permissions and limitations 17 | // under the License. 18 | // 19 | 20 | // 21 | // XCode Build settings for "Release" Build Configuration. 22 | // 23 | 24 | HEADER_SEARCH_PATHS = "$(TARGET_BUILD_DIR)/usr/local/lib/include" "$(OBJROOT)/UninstalledProducts/include" "$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include" "$(BUILT_PRODUCTS_DIR)" 25 | IPHONEOS_DEPLOYMENT_TARGET = 10.0 26 | OTHER_LDFLAGS = -ObjC 27 | TARGETED_DEVICE_FAMILY = 1,2 28 | SWIFT_VERSION = 3.0 29 | 30 | // Type of signing identity used for codesigning, resolves to first match of given type. 31 | // "iPhone Developer": Development builds (default, local only; iOS Development certificate) or "iPhone Distribution": Distribution builds (Adhoc/In-House/AppStore; iOS Distribution certificate) 32 | CODE_SIGN_IDENTITY = iPhone Developer 33 | CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer 34 | 35 | // (CB-9721) Set ENABLE_BITCODE to NO in build.xcconfig 36 | ENABLE_BITCODE = NO 37 | 38 | // (CB-9719) Set CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES to YES in build.xcconfig 39 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES 40 | 41 | // (CB-10072) 42 | SWIFT_OBJC_BRIDGING_HEADER = $(PROJECT_DIR)/$(PROJECT_NAME)/Bridging-Header.h 43 | 44 | // CI Required 45 | 46 | // BluetoothManager private API requires build with embedded arm64 architecture 47 | ONLY_ACTIVE_ARCH = NO 48 | 49 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 50 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 51 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks 52 | OTHER_SWIFT_FLAGS = $(inherited) 53 | -------------------------------------------------------------------------------- /thali/install/ios/build-ci.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one 3 | // or more contributor license agreements. See the NOTICE file 4 | // distributed with this work for additional information 5 | // regarding copyright ownership. The ASF licenses this file 6 | // to you under the Apache License, Version 2.0 (the 7 | // "License"); you may not use this file except in compliance 8 | // with the License. You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, 13 | // software distributed under the License is distributed on an 14 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | // KIND, either express or implied. See the License for the 16 | // specific language governing permissions and limitations 17 | // under the License. 18 | // 19 | 20 | // 21 | // XCode Build settings for "Debug" Build Configuration. 22 | // 23 | 24 | HEADER_SEARCH_PATHS = "$(TARGET_BUILD_DIR)/usr/local/lib/include" "$(OBJROOT)/UninstalledProducts/include" "$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include" "$(BUILT_PRODUCTS_DIR)" 25 | IPHONEOS_DEPLOYMENT_TARGET = 10.0 26 | OTHER_LDFLAGS = -ObjC 27 | TARGETED_DEVICE_FAMILY = 1,2 28 | SWIFT_VERSION = 3.0 29 | 30 | // Type of signing identity used for codesigning, resolves to first match of given type. 31 | // "iPhone Developer": Development builds (default, local only; iOS Development certificate) or "iPhone Distribution": Distribution builds (Adhoc/In-House/AppStore; iOS Distribution certificate) 32 | CODE_SIGN_IDENTITY = iPhone Developer 33 | CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer 34 | 35 | // (CB-9721) Set ENABLE_BITCODE to NO in build.xcconfig 36 | ENABLE_BITCODE = NO 37 | 38 | // (CB-9719) Set CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES to YES in build.xcconfig 39 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES 40 | 41 | // (CB-10072) 42 | SWIFT_OBJC_BRIDGING_HEADER = $(PROJECT_DIR)/$(PROJECT_NAME)/Bridging-Header.h 43 | 44 | // CI Required 45 | 46 | // BluetoothManager private API requires build with embedded arm64 architecture 47 | ONLY_ACTIVE_ARCH = NO 48 | 49 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 50 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 TEST=1 51 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks 52 | OTHER_SWIFT_FLAGS = $(inherited) '-DTEST' 53 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/testPouchDBGenerator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tape = require('../lib/thaliTape'); 4 | var testUtils = require('../lib/testUtils.js'); 5 | 6 | var fs = require('fs-extra-promise'); 7 | var path = require('path'); 8 | var PouchDB = require('pouchdb'); 9 | var PouchDBGenerator = require('thali/NextGeneration/utils/pouchDBGenerator'); 10 | var leveldownMobile = require('leveldown-mobile'); 11 | 12 | // DB defaultDirectory should be unique among all tests 13 | // and any instance of this test. 14 | // This is especially required for tape.coordinated. 15 | var defaultDirectory = path.join( 16 | testUtils.getPouchDBTestDirectory(), 17 | 'pouch-db-generator-db-' + testUtils.getRandomPouchDBName() 18 | ); 19 | 20 | var test = tape({ 21 | setup: function (t) { 22 | fs.ensureDirSync(defaultDirectory); 23 | t.end(); 24 | }, 25 | teardown: function (t) { 26 | fs.removeSync(defaultDirectory); 27 | t.end(); 28 | } 29 | }); 30 | 31 | test('test defaultDirectory', function (t) { 32 | var LocalPouchDB = PouchDBGenerator(PouchDB, defaultDirectory, { 33 | defaultAdapter: leveldownMobile 34 | }); 35 | 36 | var db = LocalPouchDB('https://localhost:3000'); 37 | t.equals(db.__opts.prefix, undefined); 38 | 39 | db = LocalPouchDB('http://localhost:3000'); 40 | t.equals(db.__opts.prefix, undefined); 41 | 42 | db = LocalPouchDB('dbname'); 43 | t.equals(db.__opts.prefix, defaultDirectory); 44 | 45 | t.end(); 46 | }); 47 | 48 | test('test defaultAdapter', function (t) { 49 | var LocalPouchDB = PouchDBGenerator(PouchDB, defaultDirectory, { 50 | defaultAdapter: leveldownMobile 51 | }); 52 | 53 | var db = LocalPouchDB('https://localhost:3000'); 54 | t.equals(db._adapter, 'https'); 55 | t.equals(db.__opts.db, undefined); 56 | 57 | db = LocalPouchDB('http://localhost:3000'); 58 | t.equals(db._adapter, 'http'); 59 | t.equals(db.__opts.db, undefined); 60 | 61 | db = LocalPouchDB('dbname'); 62 | t.equals(db._adapter, 'leveldb-mobile'); 63 | t.equals(db.__opts.db, leveldownMobile); 64 | 65 | // Passing an empty function as defaultAdapter has no sense. This is just for testing. 66 | var obj = function () { 67 | return { 68 | open: function () {} 69 | }; 70 | }; 71 | LocalPouchDB = PouchDBGenerator(PouchDB, defaultDirectory, { 72 | defaultAdapter: obj 73 | }); 74 | db = LocalPouchDB('dbname'); 75 | t.equals(db._adapter, 'leveldb-mobile'); 76 | t.equals(db.__opts.db, obj); 77 | 78 | t.end(); 79 | }); 80 | -------------------------------------------------------------------------------- /test/www/jxcore/UnitTest_app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file needs to be renamed as app.js when we want to run unit tests 3 | * in order this to get loaded by the jxcore ready event. 4 | * This effectively acts as main entry point to the unit test app 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var Promise = require('bluebird'); 10 | Promise.config({ 11 | cancellation: true 12 | }); 13 | 14 | var platform = require('thali/NextGeneration/utils/platform'); 15 | 16 | if (typeof Mobile === 'undefined') { 17 | var mockPlatform = require('./lib/parsePlatformArg')(); 18 | global.Mobile = require('./lib/wifiBasedNativeMock.js')(mockPlatform); 19 | } 20 | 21 | var config = require('./config'); 22 | var objectAssign = require('object-assign'); 23 | process.env = objectAssign(process.env, config.env); 24 | 25 | var logger = require('./lib/testLogger')('UnitTest_app'); 26 | var testUtils = require('./lib/testUtils'); 27 | var ThaliMobile = require('thali/NextGeneration/thaliMobile'); 28 | 29 | var utResult = false; 30 | 31 | if (platform._isRealMobile) { 32 | Mobile('executeNativeTests').callNative(function (result) { 33 | logger.debug('Running unit tests'); 34 | if (result) { 35 | if (!result.executed) { 36 | console.log('*Native tests were not executed*'); 37 | 38 | utResult = false; 39 | } else { 40 | console.log('*Native tests were executed*'); 41 | 42 | utResult = result.failed <= 0; 43 | } 44 | 45 | console.log('Total number of executed tests: ', result.total); 46 | console.log('Number of passed tests: ', result.passed); 47 | console.log('Number of failed tests: ', result.failed); 48 | console.log('Number of ignored tests: ', result.ignored); 49 | console.log('Total duration: ', result.duration); 50 | } else { 51 | console.log('*Native tests results are empty*'); 52 | 53 | utResult = false; 54 | } 55 | }); 56 | 57 | if (!utResult) { 58 | console.log('Failed to execute UT.'); 59 | global.nativeUTFailed = true; 60 | 61 | } 62 | } else { 63 | // We aren't on a device so we can't run those tests anyway 64 | utResult = true; 65 | } 66 | 67 | if (!utResult) { 68 | logger.debug('Failed to execute UT.'); 69 | global.nativeUTFailed = true; 70 | } 71 | 72 | global.NETWORK_TYPE = ThaliMobile.networkTypes.NATIVE; 73 | 74 | Mobile('GetDeviceName').callNative(function (name) { 75 | logger.debug('My device name is: \'%s\'', name); 76 | testUtils.setName(name); 77 | require('./runTests.js'); 78 | }); 79 | 80 | logger.debug('Unit Test app is loaded'); 81 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/IncomingSocketThreadMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import android.bluetooth.BluetoothSocket; 4 | import android.util.Log; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.net.Inet4Address; 10 | import java.net.Socket; 11 | import org.thaliproject.p2p.btconnectorlib.PeerProperties; 12 | 13 | public class IncomingSocketThreadMock extends IncomingSocketThread { 14 | Long threadId = 4321L; 15 | int port; 16 | boolean closeCalled = false; 17 | InputStream tempInputStream = null; 18 | OutputStream tempOutputStream = null; 19 | boolean localStreamsCreatedSuccessfully = false; 20 | 21 | public IncomingSocketThreadMock(BluetoothSocket bluetoothSocket, Listener listener, 22 | InputStream inputStream, OutputStream outputStream) 23 | throws IOException { 24 | super(bluetoothSocket, listener, inputStream, outputStream); 25 | } 26 | 27 | public void setPort(int _port){ 28 | port = _port; 29 | } 30 | 31 | @Override 32 | public void close() { 33 | closeCalled = true; 34 | } 35 | 36 | @Override 37 | public long getId() { 38 | return threadId; 39 | } 40 | 41 | @Override 42 | public void run() { 43 | mIsClosing = false; 44 | try { 45 | Inet4Address mLocalHostAddress = (Inet4Address) Inet4Address.getByName("localhost"); 46 | 47 | mLocalhostSocket = new Socket(mLocalHostAddress, port); 48 | 49 | Log.i(mTag, "Local host address: " + getLocalHostAddressAsString() + ", port: " + 50 | getLocalHostPort()); 51 | 52 | tempInputStream = mLocalhostSocket.getInputStream(); 53 | tempOutputStream = mLocalhostSocket.getOutputStream(); 54 | localStreamsCreatedSuccessfully = true; 55 | } catch (IOException e) { 56 | Log.e(mTag, "Failed to create the local streams: " + e.getMessage(), e); 57 | mListener.onDisconnected(this, "Failed to create the local streams: " + e.getMessage()); 58 | } 59 | 60 | if (localStreamsCreatedSuccessfully) { 61 | Log.d(mTag, "Setting local streams and starting stream copying threads..."); 62 | mLocalInputStream = tempInputStream; 63 | mLocalOutputStream = tempOutputStream; 64 | 65 | startStreamCopyingThreads(new ConnectionData( 66 | new PeerProperties(PeerProperties.BLUETOOTH_MAC_ADDRESS_UNKNOWN), true)); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /thali/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thali", 3 | "version": "2.1.0", 4 | "description": "Thali Cordova Plugin", 5 | "main": "thalireplicationmanager.js", 6 | "dependencies": { 7 | "bluebird": "3.4.6", 8 | "body-parser": "1.13.3", 9 | "express": "4.13.3", 10 | "forever-agent": "https://github.com/thaliproject/forever-agent.git#f9a92c7a1b7ce4da849beca32b0ec2aeb855e0fd", 11 | "ip": "1.0.1", 12 | "javascript-state-machine": "2.3.5", 13 | "lie": "3.1.0", 14 | "long": "3.0.3", 15 | "multiplex": "6.7.0", 16 | "node-ssdp": "https://github.com/thaliproject/node-ssdp.git#3a5909e201aee401f48965e378eb2ff0e2f9e027", 17 | "object-assign": "4.1.0", 18 | "pouchdb-adapter-leveldb-core": "6.1.1", 19 | "request": "2.64.0", 20 | "salti": "https://github.com/thaliproject/salti.git#master", 21 | "urlsafe-base64": "1.0.0", 22 | "uuid": "2.0.2", 23 | "winston": "2.2.0" 24 | }, 25 | "devDependencies": { 26 | "jsdoc": "3.3.3", 27 | "eslint": "3.11.1" 28 | }, 29 | "thaliInstall": { 30 | "thali": { 31 | "projectName": "thaliproject", 32 | "depotName": "Thali_CordovaPlugin", 33 | "branchName": "iOS" 34 | }, 35 | "androidConfig": { 36 | "minSdkVersion": "21", 37 | "buildToolsVersion": "25.0.2", 38 | "compileSdkVersion": "android-25" 39 | }, 40 | "btconnectorlib2": "0.3.9", 41 | "jxcore-cordova": "0.1.14", 42 | "jxcore-cordova-url": "http://jxcore.azureedge.net/jxcore-cordova/0.1.14/release/io.jxcore.node.jx" 43 | }, 44 | "scripts": { 45 | "prepublish": "node install/prePublishThaliCordovaPlugin.js", 46 | "postpublish": "node install/postPublishThaliCordovaPlugin.js", 47 | "postinstall": "node installCordovaPlugin.js", 48 | "createPublicDocs": "./node_modules/.bin/jsdoc -c conf.json .", 49 | "createInternalDocs": "./node_modules/.bin/jsdoc -c conf.json -p ." 50 | }, 51 | "repository": { 52 | "type": "git", 53 | "url": "git+https://github.com/thaliproject/Thali_CordovaPlugin.git" 54 | }, 55 | "keywords": [ 56 | "Thali", 57 | "JXCore", 58 | "PouchDB", 59 | "Multiplex", 60 | "cordova", 61 | "Plugin", 62 | "P2P", 63 | "BlueTooth", 64 | "BLE", 65 | "Multipeer Connectivity Framework", 66 | "node.js", 67 | "Android", 68 | "iOS", 69 | "Cordova" 70 | ], 71 | "author": "Microsoft Corporation", 72 | "license": "MIT", 73 | "bugs": { 74 | "url": "https://github.com/thaliproject/Thali_CordovaPlugin/issues" 75 | }, 76 | "homepage": "https://github.com/thaliproject/Thali_CordovaPlugin/tree/story_00#readme" 77 | } 78 | -------------------------------------------------------------------------------- /test/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 35 | 36 | Hello World 37 | 38 | 39 |
40 |

Thali Test App

41 |
42 |

Loading...

43 | 44 | 46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/testMultiplex.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tcpmultiplex = require('thali/tcpmultiplex'); 4 | var tape = require('../../lib/thaliTape'); 5 | var net = require('net'); 6 | var randomstring = require('randomstring'); 7 | var multiplex = require('multiplex'); 8 | 9 | var test = tape({ 10 | setup: function(t) { 11 | t.end(); 12 | }, 13 | teardown: function(t) { 14 | t.end(); 15 | } 16 | }); 17 | 18 | test('multiplex can send data', function (t) { 19 | var len = 200; 20 | var testMessage = randomstring.generate(len); 21 | 22 | var server = net.createServer(function (socket) { 23 | var plex1 = multiplex(function (stream) { 24 | stream.on('data', function (data) { 25 | t.equal(testMessage, String(data), 'String should be length:' + testMessage.length); 26 | t.end(); 27 | 28 | socket.destroy(); 29 | server.close(); 30 | }); 31 | }); 32 | 33 | socket.pipe(plex1).pipe(socket); 34 | }); 35 | 36 | server.listen(0, function () { 37 | var serverPort = server.address().port; 38 | var socket = net.createConnection({port: serverPort}, function () { 39 | var plex2 = multiplex(); 40 | var stream = plex2.createStream(); 41 | stream.write(new Buffer(testMessage)); 42 | 43 | plex2.pipe(socket).pipe(plex2); 44 | }); 45 | }); 46 | }); 47 | /* 48 | test('muxServerBridge', function (t) { 49 | var len = 200; 50 | var testMessage = randomstring.generate(len); 51 | 52 | var testServer = net.createServer(function (socket) { 53 | socket.pipe(socket); 54 | }); 55 | 56 | testServer.listen(0, function () { 57 | var testServerPort = testServer.address().port; 58 | var muxServerBridge = tcpmultiplex.muxServerBridge(testServerPort); 59 | muxServerBridge.listen(function () { 60 | var serverPort = muxServerBridge.address().port; 61 | 62 | var muxClientBridge = tcpmultiplex.muxClientBridge(serverPort, function (err) { 63 | 64 | muxClientBridge.listen(function () { 65 | var clientPort = muxClientBridge.address().port; 66 | 67 | var socket = net.createConnection({port: clientPort}, function () { 68 | socket.end(new Buffer(testMessage)); 69 | }); 70 | 71 | socket.on('data', function (data) { 72 | t.equal(testMessage, String(data), 'String should be length:' + testMessage.length); 73 | t.end(); 74 | 75 | muxClientBridge.on('close', function () { 76 | console.log('closed'); 77 | }); 78 | 79 | muxClientBridge.close(); 80 | muxServerBridge.close(); 81 | server.close(); 82 | }); 83 | }); 84 | }); 85 | }); 86 | }); 87 | }); 88 | */ 89 | -------------------------------------------------------------------------------- /thali/NextGeneration/thaliConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint multistr: true */ 4 | 5 | var BOGUS_CERT_PEM = '-----BEGIN CERTIFICATE-----\n\ 6 | MIICKjCCAZMCCQDQ8o4kHKdCPDANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV\n\ 7 | UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO\n\ 8 | BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA\n\ 9 | dGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEyWjB9\n\ 10 | MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK\n\ 11 | EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MTEgMB4G\n\ 12 | CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEFAANL\n\ 13 | ADBIAkEAnzpAqcoXZxWJz/WFK7BXwD23jlREyG11x7gkydteHvn6PrVBbB5yfu6c\n\ 14 | bk8w3/Ar608AcyMQ9vHjkLQKH7cjEQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAKha\n\ 15 | HqjCfTIut+m/idKy3AoFh48tBHo3p9Nl5uBjQJmahKdZAaiksL24Pl+NzPQ8LIU+\n\ 16 | FyDHFp6OeJKN6HzZ72Bh9wpBVu6Uj1hwhZhincyTXT80wtSI/BoUAW8Ls2kwPdus\n\ 17 | 64LsJhhxqj2m4vPKNRbHB2QxnNrGi30CUf3kt3Ia\n\ 18 | -----END CERTIFICATE-----'; 19 | 20 | var BOGUS_KEY_PEM = '-----BEGIN RSA PRIVATE KEY-----\n\ 21 | MIIBOwIBAAJBAJ86QKnKF2cVic/1hSuwV8A9t45URMhtdce4JMnbXh75+j61QWwe\n\ 22 | cn7unG5PMN/wK+tPAHMjEPbx45C0Ch+3IxECAwEAAQJBAI2cU1IuR+4IO87WPyAB\n\ 23 | 76kruoo87AeNQkjjvuQ/00+b/6IS45mcEP5Kw0NukbqBhIw2di9uQ9J51DJ/ZfQr\n\ 24 | +YECIQDUHaN3ZjIdJ7/w8Yq9Zzz+3kY2F/xEz6e4ftOFW8bY2QIhAMAref+WYckC\n\ 25 | oECgOLAvAxB1lI4j7oCbAaawfxKdnPj5AiEAi95rXx09aGpAsBGmSdScrPdG1v6j\n\ 26 | 83/2ebrvoZ1uFqkCIB0AssnrRVjUB6GZTNTyU3ERfdkx/RX1zvr8WkFR/lXpAiB7\n\ 27 | cUZ1i8ZkZrPrdVgw2cb28UJM7qZHQnXcMHTXFFvxeQ==\n\ 28 | -----END RSA PRIVATE KEY-----'; 29 | 30 | module.exports = { 31 | SSDP_IP: '228.227.226.225', 32 | SSDP_NT: process.env.SSDP_NT || 'http://www.thaliproject.org/ssdp', 33 | SSDP_ADVERTISEMENT_INTERVAL: 500, 34 | SSDP_OWN_PEERS_HISTORY_SIZE: 10, 35 | PEER_AVAILABILITY_WATCHER_INTERVAL: 1000, 36 | TCP_PEER_UNAVAILABILITY_THRESHOLD: 5 * 500, 37 | TCP_TIMEOUT_WIFI: 1000, 38 | TCP_TIMEOUT_BLUETOOTH: 5000, 39 | TCP_TIMEOUT_MPCF: 5000, 40 | NOTIFICATION_BEACON_PATH: '/NotificationBeacons', 41 | BEACON_MILLISECONDS_TO_EXPIRE: 60 * 60 * 1000, 42 | BASE_DB_PATH: '/db', 43 | SUPPORTED_PSK_CIPHERS: 'PSK-AES256-CBC-SHA', 44 | BEACON_CURVE: 'secp256k1', 45 | MAXIMUM_NATIVE_PEERS_CREATE_PEER_LISTENER_ADVERTISES: 20, 46 | BOGUS_CERT_PEM: BOGUS_CERT_PEM, 47 | BOGUS_KEY_PEM: BOGUS_KEY_PEM, 48 | BEACON_PSK_IDENTITY: 'Beacon Please', 49 | BEACON_KEY: new Buffer('Let me in please!!'), 50 | MAX_NOTIFICATIONSERVER_PSK_MAP_CACHE_SIZE: 50, 51 | LOCAL_SEQ_POINT_PREFIX: 'thali_', 52 | UPDATE_WINDOWS_FOREGROUND_MS: 1000, 53 | UPDATE_WINDOWS_BACKGROUND_MS: 10000, 54 | MAXIMUM_NUMBER_OF_PEERS_TO_NOTIFY: 15, 55 | MULTI_PEER_CONNECTIVITY_FRAMEWORK_PEERS_LIMIT: 200, 56 | BLUETOOTH_PEERS_LIMIT: 200, 57 | TCP_NATIVE_PEERS_LIMIT: 200 58 | }; 59 | -------------------------------------------------------------------------------- /src/android/java/io/jxcore/node/JXcoreThaliCallback.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2016 Microsoft Corporation. This software is licensed under the MIT License. 2 | * See the license file delivered with this project for further information. 3 | */ 4 | package io.jxcore.node; 5 | 6 | /** 7 | * A utility class for delayed/asynchronous JXcore callbacks. 8 | */ 9 | abstract class JXcoreThaliCallback { 10 | private final ListenerOrIncomingConnection mListenerOrIncomingConnection = new ListenerOrIncomingConnection(); 11 | private String mErrorMessage = null; 12 | 13 | /** 14 | * @return The ListenerOrIncomingConnection instance. Guaranteed not be null. 15 | */ 16 | public ListenerOrIncomingConnection getListenerOrIncomingConnection() { 17 | return mListenerOrIncomingConnection; 18 | } 19 | 20 | public String getErrorMessage() { 21 | return mErrorMessage; 22 | } 23 | 24 | public void setErrorMessage(String errorMessage) { 25 | mErrorMessage = errorMessage; 26 | } 27 | 28 | public void callOnConnectCallback( 29 | final String errorMessage, final ListenerOrIncomingConnection listenerOrIncomingConnection) { 30 | jxcore.activity.runOnUiThread(new Runnable() { 31 | @Override 32 | public void run() { 33 | onConnectCallback(errorMessage, listenerOrIncomingConnection); 34 | } 35 | }); 36 | } 37 | 38 | public void callOnStartStopCallback(final String errorMessage) { 39 | jxcore.activity.runOnUiThread(new Runnable() { 40 | @Override 41 | public void run() { 42 | onStartStopCallback(errorMessage); 43 | } 44 | }); 45 | } 46 | 47 | /** 48 | * If err is not NULL then listenerOrIncomingConnection MUST be null and vice 49 | * versa. 50 | * 51 | * @param errorMessage If null then the call the callback was submitted to was 52 | * successful. If not null then it will be an Error object that will define what 53 | * went wrong. 54 | * @param listenerOrIncomingConnection If null then the call the callback was 55 | * submitted to failed. Otherwise this 56 | * contains the success results. 57 | */ 58 | protected void onConnectCallback( 59 | String errorMessage, ListenerOrIncomingConnection listenerOrIncomingConnection) { 60 | // No default implementation 61 | } 62 | 63 | /** 64 | * @param errorMessage If null, the operation was successful. 65 | */ 66 | protected void onStartStopCallback(String errorMessage) { 67 | // No default implementation 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/www/jxcore/perf_tests/BluetoothConnectionLimits.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var events = require('events'); 4 | var ThaliEmitter = require('thali/thaliemitter'); 5 | 6 | /** @module BluetoothConnectionLimits */ 7 | 8 | /** 9 | * @file 10 | * 11 | * The problem we face is that we fundamentally don't know how the heck Bluetooth actually works on Android phones. 12 | * By way of background Bluetooth is based on something called a piconet. A piconet consists of a boss with up to 7 13 | * workers. Bluetooth is a time division multiplexing protocol and all the devices in the same piconet use the boss's 14 | * clock to decide how to grab time slots and the boss tells the workers when they are allowed to speak. To complicate 15 | * matters further there can actually be up to 256 devices in a piconet but beyond those 8 the rest have to be 16 | * inactive. This means in effective they are asleep. The boss is allowed to wake up devices but if they are at 17 | * the maximum of 7 active devices then they have to move an active device to being inactive. 18 | * 19 | * A single device can be boss on exactly one piconet but it can be a worker on a theoretically unlimited number of 20 | * piconets. In practice however the probability of message collisions go up the more devices and piconets there are 21 | * so at some point the density of devices would get high enough to seriously interfere with communications. 22 | * 23 | * So here is what we don't know: 24 | * - When device A and device B establish an insecure RFCOMM connection how do they decide what piconet to use? 25 | * - If device A already has a connection with device B and then device B establishes a connection with device A does 26 | * this go over the existing insecure RFCOMM connection or is a new connection created? If a new connection is created 27 | * does it go over the existing piconet or on a new piconet? 28 | * - How many piconets can a device support being a worker on? 29 | * 30 | * We can boil this down to a set of specific questions we need answers to: 31 | * 1. How many simultaneous incoming connections can we support? 32 | * 2. How many simultaneous outgoing connections can we support? 33 | * 3. Are the answers to questions 1 and 2 related, that is, is the right question "how many connections of any type 34 | * can we simultaneously support"? 35 | * 4. If we allow a connection to go quiet will it become an inactive connection and affect the answers to questions 36 | * 1-3? 37 | * 5. Are the answers to questions 1-3 different depending on the hardware and OS we are using on the devices? 38 | */ 39 | 40 | /** 41 | * This function will determine for a device what is the maximum number of simultaneous active incoming connections 42 | * each of the devices in the test can handle. 43 | */ 44 | function testMaxIncomingActiveConnections() { 45 | 46 | } 47 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testSync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var format = util.format; 5 | 6 | var Promise = require('bluebird'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | 10 | var tape = require('../lib/thaliTape'); 11 | var testUtils = require('../lib/testUtils'); 12 | var logger = require('../lib/testLogger')('testSync'); 13 | 14 | if (!tape.coordinated) { 15 | return; 16 | } 17 | 18 | 19 | // We will use a files for data verification. 20 | var localData = { 21 | path: path.join(testUtils.tmpDirectory(), 'testSync.txt'), 22 | timeout: Math.floor(Math.random() * 100) 23 | }; 24 | 25 | var test = tape({ 26 | setup: function (t) { 27 | t.data = localData; 28 | t.end(); 29 | }, 30 | teardown: function (t) { 31 | t.end(); 32 | } 33 | }); 34 | 35 | function checkAllFiles(participants) { 36 | var promises = participants.map(function (participant) { 37 | var path = participant.data.path; 38 | return new Promise(function (resolve, reject) { 39 | fs.stat(path, function (error, stats) { 40 | if (error || !stats.isFile()) { 41 | reject(new Error( 42 | format('file does not exist, path: \'%s\'', path) 43 | )); 44 | } else { 45 | logger.debug('file exist, path: \'%s\'', path); 46 | resolve(); 47 | } 48 | }); 49 | }); 50 | }); 51 | return Promise.all(promises); 52 | } 53 | 54 | function syncWorks (t) { 55 | return new Promise(function (resolve) { 56 | setTimeout(resolve, localData.timeout); 57 | }) 58 | .then(function () { 59 | // we are creating local file at random period of time. 60 | return fs.writeFile(localData.path, ''); 61 | }) 62 | .then(function () { 63 | logger.debug('file created, path: \'%s\'', localData.path); 64 | return t.sync(); 65 | }) 66 | .then(function () { 67 | return checkAllFiles(t.participants); 68 | }) 69 | .then(function () { 70 | return t.sync(); 71 | }) 72 | .then(function () { 73 | fs.unlinkSync(localData.path); 74 | logger.debug('file removed, path: \'%s\'', localData.path); 75 | }); 76 | } 77 | 78 | test('test sync works', function (t) { 79 | syncWorks(t) 80 | .then(function () { 81 | t.pass('passed'); 82 | }) 83 | .catch(function (error) { 84 | t.fail('failed with ' + error.toString()); 85 | }) 86 | .then(function () { 87 | t.end(); 88 | }); 89 | }); 90 | 91 | test('test multiple syncs works', function (t) { 92 | syncWorks(t) 93 | .then(function () { 94 | return syncWorks(t); 95 | }) 96 | .then(function () { 97 | t.pass('passed'); 98 | }) 99 | .catch(function (error) { 100 | t.fail('failed with ' + error.toString()); 101 | }) 102 | .then(function () { 103 | t.end(); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /www/android/thaliPermissions.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 Microsoft 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // Permissions Cordova Plugin 24 | // ThaliPermissions.js 25 | 26 | 'use strict'; 27 | 28 | /** @module thaliPermissions */ 29 | 30 | /** 31 | * Response codes for the permission request. 32 | * @readonly 33 | * @enum {string} 34 | */ 35 | module.exports.responseCodes = { 36 | /** User has denied access to requested permission */ 37 | 'PERMISSION_DENIED': 'PERMISSION_DENIED', 38 | /** Concurrent request are not supported*/ 39 | 'RESPONSE_CONCURRENT_PERMISSION_REQUESTS_NOT_SUPPORTED': 40 | 'RESPONSE_CONCURRENT_PERMISSION_REQUESTS_NOT_SUPPORTED' 41 | }; 42 | 43 | /** 44 | * This callback function is called if the location permission is granted. 45 | * @public 46 | * @callback successCallbackFn 47 | */ 48 | 49 | /** 50 | * This callback function is called if the location permission is not granted. 51 | * @public 52 | * @callback errorCallbackFn 53 | * @param {module:thaliPermissions~responseCodes} 54 | */ 55 | 56 | /** 57 | * Beginning in Android 6.0, users grant permissions to apps while the app is 58 | * running. This function can be used to check whether the user has 59 | * granted the location permission for the application. 60 | * If the location permission is not granted, and the user hasn't checked 61 | * "Never ask again" for a permission query dialog system will display 62 | * the permission query dialog for the user. 63 | * @param {module:thaliPermissions~successCallbackFn} successFn 64 | * @param {module:thaliPermissions~errorCallbackFn} errorFn 65 | */ 66 | module.exports.requestLocationPermission = function (successFn, errorFn) { 67 | cordova.exec(successFn, errorFn, 'ThaliPermissions', 68 | 'REQUEST_ACCESS_COARSE_LOCATION', []); 69 | }; 70 | -------------------------------------------------------------------------------- /test/TestServer/HttpServer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var inherits = util.inherits; 5 | 6 | var http = require('http'); 7 | var socketIO = require('socket.io'); 8 | var objectAssign = require('object-assign'); 9 | var EventEmitter = require('events').EventEmitter; 10 | 11 | var asserts = require('./utils/asserts'); 12 | var logger = require('./utils/ThaliLogger')('HttpServer'); 13 | 14 | var TestDevice = require('./TestDevice'); 15 | 16 | 17 | function Server (options) { 18 | var self = this; 19 | 20 | this._setOptions(options); 21 | 22 | var server = http.createServer(); 23 | this._io = socketIO(server, { 24 | transports: this._options.transports 25 | }); 26 | server.listen(this._options.port, function () { 27 | logger.info('listening on *:' + self._options.port); 28 | }); 29 | 30 | this._bind(); 31 | } 32 | 33 | inherits(Server, EventEmitter); 34 | 35 | Server.prototype.defaults = { 36 | transports: ['websocket'] 37 | }; 38 | 39 | Server.prototype._setOptions = function (options) { 40 | asserts.isObject(options, 'Server._setOptions'); 41 | 42 | this._options = objectAssign({}, this.defaults, options); 43 | 44 | asserts.isNumber(this._options.port); 45 | asserts.isArray(this._options.transports); 46 | this._options.transports.forEach(function (transport) { 47 | asserts.isString(transport); 48 | }); 49 | }; 50 | 51 | Server.prototype._bind = function () { 52 | process.once('exit', this._exit.bind(this)); 53 | this._io.on('connect', this._connect.bind(this)); 54 | }; 55 | 56 | Server.prototype._connect = function (socket) { 57 | asserts.exists(socket); 58 | socket.deviceName = 'device that was not presented yet'; 59 | 60 | socket 61 | .on('disconnect', this._disconnect.bind(this, socket)) 62 | .on('error', this._error.bind(this, socket)) 63 | .on('present', this._present.bind(this, socket)); 64 | }; 65 | 66 | Server.prototype._disconnect = function (socket, reason) { 67 | logger.info( 68 | 'Socket to device name: \'%s\' disconnected, reason: \'%s\'', 69 | socket.deviceName, reason 70 | ); 71 | }; 72 | 73 | Server.prototype._error = function (socket, error) { 74 | logger.error( 75 | 'unexpected server error: \'%s\'', 76 | error.content 77 | ); 78 | throw new Error(error.content); 79 | }; 80 | 81 | Server.prototype._present = function (socket, deviceInfo) { 82 | var device = new TestDevice(socket, deviceInfo); 83 | socket.deviceName = device.name; 84 | 85 | logger.debug( 86 | 'device presented, name: \'%s\', uuid: \'%s\', platformName: \'%s\', ' + 87 | 'type: \'%s\', hasRequiredHardware: \'%s\', nativeUTFailed: \'%s\'', 88 | device.name, device.uuid, device.platformName, device.type, 89 | device.hasRequiredHardware, device.nativeUTFailed 90 | ); 91 | 92 | this.emit('present', device); 93 | }; 94 | 95 | Server.prototype._exit = function () { 96 | this._io.close(); 97 | }; 98 | 99 | module.exports = Server; 100 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/utils/asserts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var format = require('util').format; 4 | var assert = require('assert'); 5 | 6 | 7 | function exists(value) { 8 | return value !== undefined && value !== null; 9 | } 10 | module.exports.exists = function (value) { 11 | assert( 12 | exists(value), 13 | format('existing value expected, received: \'%s\'', value) 14 | ); 15 | }; 16 | 17 | function isString(value) { 18 | return exists(value) && typeof value === 'string'; 19 | } 20 | module.exports.isString = function (value) { 21 | assert( 22 | isString(value), 23 | format('string expected, received: \'%s\'', value) 24 | ); 25 | }; 26 | 27 | function isArray(value) { 28 | return exists(value) && Array.isArray(value); 29 | } 30 | module.exports.isArray = function (value) { 31 | assert( 32 | isArray(value), 33 | format('array expected, received: \'%s\'', value) 34 | ); 35 | }; 36 | 37 | function isBool(value) { 38 | return value === true || value === false; 39 | } 40 | module.exports.isBool = function (value) { 41 | assert( 42 | isBool(value), 43 | format('bool expected, received: \'%s\'', value) 44 | ); 45 | }; 46 | 47 | function isNumber(value) { 48 | return exists(value) && typeof value === 'number'; 49 | } 50 | module.exports.isNumber = function (value) { 51 | assert( 52 | isNumber(value), 53 | format('number expected, received: \'%s\'', value) 54 | ); 55 | }; 56 | 57 | function arrayEquals(a1, a2) { 58 | if ( 59 | !isArray(a1) || !isArray(a2) || 60 | a1.length !== a2.length 61 | ) { 62 | return false; 63 | } 64 | return a1.every(function (value, key) { 65 | return value === a2[key]; 66 | }); 67 | } 68 | module.exports.arrayEquals = function (a1, a2) { 69 | assert( 70 | arrayEquals(a1, a2), 71 | format('equals arrays expected, received array 1: \'%s\', array 2: \'%s\'', 72 | a1, a2) 73 | ); 74 | }; 75 | 76 | function isObject(value) { 77 | return exists(value) && typeof value === 'object'; 78 | } 79 | module.exports.isObject = function (value) { 80 | assert( 81 | isObject(value), 82 | format('object expected, received: \'%s\'', value) 83 | ); 84 | }; 85 | 86 | function instanceOf(value, base) { 87 | return isObject(value) && value instanceof base; 88 | } 89 | module.exports.instanceOf = function (value, base) { 90 | assert( 91 | instanceOf(value, base), 92 | format('\'%s\' should be an instance of \'%s\'', value, base) 93 | ); 94 | }; 95 | 96 | module.exports.equals = function (value1, value2) { 97 | assert( 98 | value1 === value2, 99 | format('equals values expected, received value 1: \'%s\', value 2: \'%s\'', 100 | value1, value2) 101 | ); 102 | }; 103 | 104 | function isFunction(fun) { 105 | return exists(fun) && typeof fun === 'function'; 106 | } 107 | module.exports.isFunction = function (value) { 108 | assert( 109 | isFunction(value), 110 | format('function expected, received: \'%s\'', value) 111 | ); 112 | }; 113 | -------------------------------------------------------------------------------- /test/TestServer/utils/asserts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var format = require('util').format; 4 | var assert = require('assert'); 5 | 6 | 7 | function exists(value) { 8 | return value !== undefined && value !== null; 9 | } 10 | module.exports.exists = function (value) { 11 | assert( 12 | exists(value), 13 | format('existing value expected, received: \'%s\'', value) 14 | ); 15 | }; 16 | 17 | function isString(value) { 18 | return exists(value) && typeof value === 'string'; 19 | } 20 | module.exports.isString = function (value) { 21 | assert( 22 | isString(value), 23 | format('string expected, received: \'%s\'', value) 24 | ); 25 | }; 26 | 27 | function isArray(value) { 28 | return exists(value) && Array.isArray(value); 29 | } 30 | module.exports.isArray = function (value) { 31 | assert( 32 | isArray(value), 33 | format('array expected, received: \'%s\'', value) 34 | ); 35 | }; 36 | 37 | function isBool(value) { 38 | return value === true || value === false; 39 | } 40 | module.exports.isBool = function (value) { 41 | assert( 42 | isBool(value), 43 | format('bool expected, received: \'%s\'', value) 44 | ); 45 | }; 46 | 47 | function isNumber(value) { 48 | return exists(value) && typeof value === 'number'; 49 | } 50 | module.exports.isNumber = function (value) { 51 | assert( 52 | isNumber(value), 53 | format('number expected, received: \'%s\'', value) 54 | ); 55 | }; 56 | 57 | function arrayEquals(a1, a2) { 58 | if ( 59 | !isArray(a1) || !isArray(a2) || 60 | a1.length !== a2.length 61 | ) { 62 | return false; 63 | } 64 | return a1.every(function (value, key) { 65 | return value === a2[key]; 66 | }); 67 | } 68 | module.exports.arrayEquals = function(a1, a2) { 69 | assert( 70 | arrayEquals(a1, a2), 71 | format('equals arrays expected, received array 1: \'%s\', array 2: \'%s\'', a1, a2) 72 | ); 73 | } 74 | 75 | function isObject(value) { 76 | return exists(value) && typeof value === 'object'; 77 | } 78 | module.exports.isObject = function (value, message) { 79 | assert( 80 | isObject(value), 81 | format('object expected, received: \'%s\' \'%s\'', value, 82 | message ? ('-' + message) : '') 83 | ); 84 | }; 85 | 86 | function instanceOf(value, base) { 87 | return isObject(value) && value instanceof base; 88 | } 89 | module.exports.instanceOf = function (value, base) { 90 | assert( 91 | instanceOf(value, base), 92 | format('\'%s\' should be an instance of \'%s\'', value, base) 93 | ); 94 | }; 95 | 96 | module.exports.equals = function (value1, value2) { 97 | assert( 98 | value1 === value2, 99 | format('equals values expected, received value 1: \'%s\', value 2: \'%s\'', value1, value2) 100 | ); 101 | } 102 | 103 | function isFunction(fun) { 104 | return exists(fun) && typeof fun === 'function'; 105 | } 106 | module.exports.isFunction = function (value) { 107 | assert( 108 | isFunction(value), 109 | format('function expected, received: \'%s\'', value) 110 | ); 111 | }; 112 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/IdentityExchange/testConnectionTable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tape = require('../lib/thaliTape'); 4 | var ConnectionTable = require('thali/identityExchange/connectionTable'); 5 | var ThaliReplicationManager = require('thali/thalireplicationmanager'); 6 | 7 | // test setup & teardown activities 8 | var test = tape({ 9 | setup: function (t) { 10 | t.end(); 11 | }, 12 | teardown: function(t) { 13 | t.end(); 14 | } 15 | }); 16 | 17 | function createAnnounce(peerId, muxPort) { 18 | return { peerIdentifier: peerId, muxPort: muxPort }; 19 | } 20 | 21 | var peersToAnnounce = [ 22 | createAnnounce("a", 30), 23 | createAnnounce("b", 40), 24 | createAnnounce("a", 50), 25 | createAnnounce("c", 60) 26 | ]; 27 | 28 | var results = [ 29 | { peerId: "a", port: 50 }, 30 | { peerId: "b", port: 40 }, 31 | { peerId: "c", port: 60 } 32 | ]; 33 | 34 | test('test connectionTable table building and cleanup', function (t) { 35 | var thaliReplicationManager = new ThaliReplicationManager("bogus", "bogus"); 36 | var connectionTable = new ConnectionTable(thaliReplicationManager); 37 | 38 | var timeBeforeEmit = Date.now(); 39 | 40 | peersToAnnounce.forEach(function(announce) { 41 | thaliReplicationManager.emit(ThaliReplicationManager.events.CONNECTION_SUCCESS, announce); 42 | }); 43 | 44 | var timeAfterEmit = Date.now(); 45 | 46 | results.forEach(function(result) { 47 | var lookup = connectionTable.lookUpPeerId(result.peerId); 48 | t.equal(lookup.muxPort, result.port); 49 | t.ok(lookup.time >= timeBeforeEmit && lookup.time <= timeAfterEmit); 50 | }); 51 | 52 | t.equal(null, connectionTable.lookUpPeerId("d")); 53 | t.equal(null, connectionTable.lookUpPeerId("a", Date.now() + 100)); 54 | t.ok(connectionTable.lookUpPeerId("c", 0)); 55 | 56 | connectionTable.cleanUp(); 57 | t.throws(function() { connectionTable.lookUpPeerId("a") }, connectionTable.cleanUpCalledErrorMessage); 58 | t.end(); 59 | }); 60 | 61 | test('test connectionTable emitting events for peerIds', function(t) { 62 | var thaliReplicationManager = new ThaliReplicationManager("bogus", "bogus"); 63 | var connectionTable = new ConnectionTable(thaliReplicationManager); 64 | 65 | var connectionTableListenersThatRan = 0; 66 | 67 | peersToAnnounce.forEach(function(announce) { 68 | var timeBeforeEmit = Date.now() 69 | var listener = function(tableEntry) { 70 | t.equal(announce.muxPort, tableEntry.muxPort); 71 | t.ok(tableEntry.time >= timeBeforeEmit && tableEntry.time <= timeBeforeEmit + 100); 72 | 73 | connectionTable.removeListener(announce.peerIdentifier, listener); 74 | 75 | connectionTableListenersThatRan += 1; 76 | if (connectionTableListenersThatRan == peersToAnnounce.length) { 77 | t.end(); 78 | } 79 | }; 80 | connectionTable.on(announce.peerIdentifier, listener); 81 | thaliReplicationManager.emit(ThaliReplicationManager.events.CONNECTION_SUCCESS, announce); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /test/www/jxcore/PerfTest_app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file needs to be renamed as app.js when we want to run performance tests 3 | * in order this to get loaded by the jxcore ready event. 4 | * This effectively acts as main entry point to the performance test app 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var testUtils = require('./lib/testUtils'); 10 | var TestFrameworkClient = require('./perf_tests/PerfTestFrameworkClient'); 11 | var platform = require('thali/NextGeneration/utils/platform'); 12 | 13 | /* ----------------------------------------------------------------------------- 14 | code for connecting to the coordinator server 15 | -----------------------------------------------------------------------------*/ 16 | 17 | function getDeviceCharacteristics(cb) { 18 | 19 | if (typeof jxcore === 'undefined') { 20 | cb('PERF_TEST-' + Math.random(), null); 21 | } 22 | else if (platform.isAndroid) { 23 | Mobile('GetBluetoothAddress').callNative( 24 | function (bluetoothAddressError, bluetoothAddress) { 25 | Mobile('GetBluetoothName').callNative( 26 | function (bluetoothNameError, bluetoothName) { 27 | Mobile('GetDeviceName').callNative( 28 | function (deviceName) { 29 | console.log('Received device characteristics:\n' + 30 | 'Bluetooth address: ' + bluetoothAddress + '\n' + 31 | 'Bluetooth name: ' + bluetoothName + '\n' + 32 | 'Device name: ' + deviceName); 33 | // In case of Android, the name used is first checked from the 34 | // Bluetooth name, because that is one that user can set. If that is 35 | // not set, the returned device name is used. The device name is not 36 | // guaranteed to be unique, because it is concatenated from device 37 | // manufacturer and model and will thus be the same in case of 38 | // identical devices. 39 | var myName = bluetoothName || deviceName; 40 | if (!myName || !bluetoothAddress) { 41 | console.log('An error while getting the device characteristics!'); 42 | } 43 | testUtils.setName(myName); 44 | cb(myName, bluetoothAddress); 45 | }); 46 | }); 47 | }); 48 | } else { 49 | Mobile('GetDeviceName').callNative(function (deviceName) { 50 | // In case of iOS, the device name is used directly, because 51 | // the one returned in the one that user can set. 52 | testUtils.setName(deviceName); 53 | cb(deviceName, null); 54 | }); 55 | } 56 | } 57 | 58 | /* ----------------------------------------------------------------------------- 59 | code for handling test communications 60 | -----------------------------------------------------------------------------*/ 61 | 62 | var testFramework; 63 | getDeviceCharacteristics(function (deviceName, bluetoothAddress) { 64 | // The test framework client will coordinate everything from here.. 65 | process.nextTick(function () { 66 | testFramework = new TestFrameworkClient(deviceName, bluetoothAddress); 67 | }); 68 | }); 69 | 70 | console.log('Perf Test app is loaded'); 71 | -------------------------------------------------------------------------------- /thali/install/cordova-hooks/ios/after_plugin_install.js: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt file in the project root 4 | // for full license information. 5 | // 6 | 7 | 'use strict'; 8 | 9 | var fs = require('fs'); 10 | var nativeInstaller = require('../../ios/nativeInstaller'); 11 | var path = require('path'); 12 | 13 | function loadIsTestEnvironment() { 14 | var utFlagFilePath = 'platforms/ios/unittests'; 15 | 16 | try { 17 | var utFlag = fs.lstatSync(utFlagFilePath); 18 | 19 | try { 20 | console.log('Removing UT flag'); 21 | 22 | fs.unlinkSync(utFlagFilePath); 23 | } catch (error) { 24 | console.log(error); 25 | console.log('Failed to remove the UT flag file, continuing anyway'); 26 | } 27 | 28 | return utFlag.isFile(); 29 | } catch (error) { 30 | console.log(error); 31 | console.log('Not a test environment, continue normally.'); 32 | } 33 | 34 | return false; 35 | } 36 | 37 | module.exports = function (context) { 38 | 39 | var isTestEnvironment = loadIsTestEnvironment(); 40 | 41 | // Need a promise so that 42 | // the install waits for us to complete our project modifications 43 | // before the plugin gets installed. 44 | var Q = context.requireCordovaModule('q'); 45 | var deferred = new Q.defer(); 46 | 47 | // Only bother if we're on macOS 48 | if (process.platform !== 'darwin') { 49 | deferred.resolve(); 50 | return deferred.promise; 51 | } 52 | 53 | var platforms = context.opts.cordova.platforms; 54 | 55 | // We can bail out if the iOS platform isn't present. 56 | if (platforms.indexOf('ios') === -1) { 57 | deferred.resolve(); 58 | return deferred.promise; 59 | } 60 | 61 | // We need to build ThaliCore.framework before embedding it into the project 62 | var iOSInfrastructureFolder = path.join( 63 | context.opts.plugin.dir, 'lib', 'ios'); 64 | var testingInfrastructureForlder = path.join( 65 | context.opts.plugin.dir, 'src', 'ios', 'Testing'); 66 | var thaliCoreProjectFolder = path.join( 67 | context.opts.plugin.dir, 'lib', 'ios', 'Carthage', 'Checkouts', 'thali-ios'); 68 | 69 | // We need to embded frameworks to the project here. 70 | // They need to be embedded binaries and cordova does not yet support that. 71 | // We will use node-xcode directy to add them since that library has 72 | // been upgraded to support embedded binaries. 73 | 74 | // Cordova libs to get the project path and project name 75 | // so we can locate the xcode project file. 76 | var cordovaUtil = 77 | context.requireCordovaModule('cordova-lib/src/cordova/util'); 78 | var ConfigParser = context.requireCordovaModule('cordova-lib').configparser; 79 | var projectRoot = cordovaUtil.isCordova(); 80 | var xml = cordovaUtil.projectConfig(projectRoot); 81 | var cfg = new ConfigParser(xml); 82 | 83 | var projectPath = path.join( 84 | projectRoot, 'platforms', 'ios', cfg.name() + '.xcodeproj'); 85 | 86 | return nativeInstaller.addFramework(projectPath, thaliCoreProjectFolder, 87 | iOSInfrastructureFolder, isTestEnvironment, testingInfrastructureForlder); 88 | }; 89 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "node": true 4 | }, 5 | "globals": { 6 | "Mobile": true 7 | }, 8 | "rules": { 9 | "no-cond-assign": 2, 10 | "eqeqeq": 2, 11 | "no-caller": 2, 12 | "no-undef": 2, 13 | "no-unused-vars": [ 14 | 2, 15 | { 16 | "vars": "local", 17 | "args": "after-used" 18 | } 19 | ], 20 | "no-eq-null": 2, 21 | "strict": [ 22 | 2, 23 | "safe" 24 | ], 25 | "no-irregular-whitespace": 2, 26 | "guard-for-in": 2, 27 | "no-unused-expressions": [ 28 | 2, 29 | { 30 | "allowShortCircuit": true, 31 | "allowTernary": true 32 | } 33 | ], 34 | "no-new": 2, 35 | "no-extra-semi": 2, 36 | "no-use-before-define": [ 37 | 2, 38 | { 39 | "functions": false 40 | } 41 | ], 42 | "no-extend-native": 2, 43 | "max-depth": [ 44 | 2, 45 | 3 46 | ], 47 | "valid-jsdoc": [ 48 | 2, 49 | { 50 | "requireReturn": false, 51 | "requireParamDescription": false, 52 | "requireReturnDescription": false 53 | } 54 | ], 55 | "max-len": [ 56 | 2, 57 | 80, 58 | 2, 59 | { 60 | "ignoreTrailingComments": true, 61 | "ignoreUrls": true, 62 | "ignoreStrings": true, 63 | "ignorePattern": getMaxLenPatterns() 64 | } 65 | ], 66 | "camelcase": [ 67 | 2, 68 | { 69 | "properties": "never" 70 | } 71 | ], 72 | "comma-style": [ 73 | 2, 74 | "last" 75 | ], 76 | "curly": [ 77 | 2, 78 | "all" 79 | ], 80 | "dot-notation": [ 81 | 2, 82 | { 83 | "allowKeywords": true 84 | } 85 | ], 86 | "operator-linebreak": [ 87 | 2, 88 | "after" 89 | ], 90 | "semi": [ 91 | 2, 92 | "always" 93 | ], 94 | "keyword-spacing": [ 95 | 2, 96 | {} 97 | ], 98 | "comma-spacing": [ 99 | 2, 100 | { 101 | "after": true 102 | } 103 | ], 104 | "spaced-comment": [ 105 | 2, 106 | "always" 107 | ], 108 | "consistent-this": [ 109 | 2, 110 | "self" 111 | ], 112 | "indent": [ 113 | 2, 114 | 2, 115 | { 116 | "SwitchCase": 1 117 | } 118 | ], 119 | "quotes": [ 120 | 2, 121 | "single" 122 | ] 123 | } 124 | }; 125 | 126 | function getMaxLenPatterns() { 127 | var patterns = [ 128 | // /** 129 | // * @param {type} identifier 130 | // */ 131 | /^\s+?\* @(param|arg|argument|prop|property) \{.+?\} (\[.+?\]|[\w\d_\.\[\]]+)$/, 132 | 133 | // /** 134 | // * {@link location} 135 | // */ 136 | /^\s+?\* \{@link .+?\}$/, 137 | 138 | // /** 139 | // * @function functionName 140 | // * @function external:"Mobile('realyLongMobileMethod')".registerToNative 141 | // */ 142 | /^\s+?\* @(function|func|method) \S+$/, 143 | 144 | // /** 145 | // * | cell1 | cell2 | 146 | // */ 147 | /^\s+?\* \|.*\|$/ 148 | ]; 149 | 150 | return patterns.map(re => `(${re.source})`).join('|'); 151 | } 152 | 153 | -------------------------------------------------------------------------------- /test/www/js/thali_main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2015-2016 Microsoft 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to 10 | // deal in the Software without restriction, including without limitation the 11 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | // sell copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | // IN THE SOFTWARE. 25 | // 26 | // 27 | // Thali Cordova Plugin thali_main.js 28 | // 29 | 30 | (function () { 31 | var inter = setInterval(function () { 32 | if (typeof jxcore === 'undefined') { return; } 33 | 34 | clearInterval(inter); 35 | 36 | jxcore.isReady(function () { 37 | if (window.ThaliPermissions) { 38 | // requestLocationPermission ensures that the application has 39 | // the required ACCESS_COARSE_LOCATION permission in Android. 40 | window.ThaliPermissions.requestLocationPermission(function () { 41 | console.log('Application has the required permission.'); 42 | loadMainFile(); 43 | }, function (error) { 44 | console.log('Location permission not granted. Error: ' + error); 45 | exitWithFailureLog(); 46 | }); 47 | } else { 48 | loadMainFile(); 49 | } 50 | }); 51 | }, 5); 52 | 53 | function loadMainFile() { 54 | jxcore('app.js').loadMainFile(function (ret, err) { 55 | if (err) { 56 | console.log('app.js file failed to load : ' + JSON.stringify(err)); 57 | exitWithFailureLog(); 58 | } else { 59 | jxcore_ready(); 60 | } 61 | }); 62 | } 63 | 64 | function jxcore_ready() { 65 | jxcore('setMyNameCallback').call(nameCallback); 66 | jxcore('setLogCallback').call(logCallback); 67 | document.getElementById('ClearLogButton').addEventListener('click', ClearLog); 68 | console.log('UIApp is all set and ready!'); 69 | } 70 | 71 | function exitWithFailureLog() { 72 | console.log('****TEST_LOGGER:[PROCESS_ON_EXIT_FAILED]****'); 73 | navigator.app.exitApp(); 74 | } 75 | 76 | function nameCallback(name) { 77 | document.getElementById('nameTag').innerHTML = name; 78 | } 79 | 80 | function ClearLog() { 81 | document.getElementById('LogBox').value = ''; 82 | } 83 | 84 | function logCallback(data) { 85 | var logBox = document.getElementById('LogBox'); 86 | logBox.value = data + '\n' + logBox.value; 87 | } 88 | }()); 89 | -------------------------------------------------------------------------------- /test/www/jxcore/lib/CoordinatedTape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var inherits = util.inherits; 5 | 6 | var objectAssign = require('object-assign'); 7 | var assert = require('assert'); 8 | var uuid = require('node-uuid'); 9 | 10 | var Promise = require('./utils/Promise'); 11 | 12 | var SimpleThaliTape = require('./SimpleTape'); 13 | var CoordinatedClient = require('./CoordinatedClient'); 14 | 15 | var logger = require('./testLogger')('CoordinatedThaliTape'); 16 | 17 | 18 | function CoordinatedThaliTape (options) { 19 | // We are calling this function directly without 'new'. 20 | if (!this) { 21 | return new CoordinatedThaliTape(options); 22 | } 23 | 24 | return CoordinatedThaliTape.super_.call(this, options); 25 | } 26 | 27 | inherits(CoordinatedThaliTape, SimpleThaliTape); 28 | 29 | CoordinatedThaliTape.states = SimpleThaliTape.states; 30 | CoordinatedThaliTape.instances = SimpleThaliTape.instances; 31 | CoordinatedThaliTape.begin = SimpleThaliTape.begin; 32 | 33 | // 'tape.uuid' here. 34 | CoordinatedThaliTape.uuid = uuid.v4(); 35 | 36 | CoordinatedThaliTape.prototype.defaults = objectAssign( 37 | {}, 38 | CoordinatedThaliTape.prototype.defaults, 39 | { 40 | emitRetryCount: 100, 41 | emitRetryTimeout: 2000 42 | } 43 | ); 44 | 45 | CoordinatedThaliTape.prototype._begin = function () { 46 | assert( 47 | this._state === CoordinatedThaliTape.states.created, 48 | 'we should be in created state' 49 | ); 50 | this._state = CoordinatedThaliTape.states.started; 51 | }; 52 | 53 | CoordinatedThaliTape.prototype._getTests = function () { 54 | var self = this; 55 | return this._tests.map(function (test) { 56 | // We don't need to copy '_options', it will be used readonly. 57 | test.options = self._options; 58 | return test; 59 | }); 60 | }; 61 | 62 | CoordinatedThaliTape.begin = function (platform, version, hasRequiredHardware, 63 | nativeUTFailed) { 64 | var tests = CoordinatedThaliTape.instances.reduce(function (tests, thaliTape) 65 | { 66 | thaliTape._begin(); 67 | return tests.concat(thaliTape._getTests()); 68 | }, []); 69 | CoordinatedThaliTape.instances = []; 70 | 71 | var _testClient = new CoordinatedClient( 72 | tests, 73 | CoordinatedThaliTape.uuid, 74 | platform, 75 | version, 76 | hasRequiredHardware, 77 | !!nativeUTFailed 78 | ); 79 | 80 | // Only used for testing purposes. 81 | CoordinatedThaliTape._testServer = _testClient._io; 82 | 83 | return new Promise(function (resolve, reject) { 84 | _testClient.once('finished', function (error) { 85 | if (error) { 86 | reject(error); 87 | } else { 88 | resolve(); 89 | } 90 | }); 91 | }) 92 | .then(function () { 93 | logger.debug('all tests succeed'); 94 | logger.debug('****TEST_LOGGER:[PROCESS_ON_EXIT_SUCCESS]****'); 95 | }) 96 | .catch(function (error) { 97 | logger.error( 98 | 'tests failed, error: \'%s\', stack: \'%s\'', 99 | error.toString(), error.stack 100 | ); 101 | logger.debug('****TEST_LOGGER:[PROCESS_ON_EXIT_FAILED]****'); 102 | return Promise.reject(error); 103 | }); 104 | }; 105 | 106 | module.exports = CoordinatedThaliTape; 107 | -------------------------------------------------------------------------------- /thali/NextGeneration/utils/common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | 5 | /** @module utils/common */ 6 | 7 | module.exports.serializePouchError = function (err) { 8 | if (err) { 9 | return (err.status || '') + ' ' + (err.message || ''); 10 | } else { 11 | return ''; 12 | } 13 | }; 14 | 15 | /** 16 | * Make function async as if it is always wrapped into setImmediate. 17 | * 18 | * For example: 19 | * 20 | * ``` 21 | * function emit(x) { 22 | * console.log(this.name + ':', 'emitting', x); 23 | * } 24 | * var emitter = { 25 | * name: 'Test emitter', 26 | * emit: emit, 27 | * emitAsync: makeAsync(emit) 28 | * }; 29 | * ``` 30 | * 31 | * is equivalent to: 32 | * 33 | * ``` 34 | * function emit(x) { 35 | * console.log(this.name + ':', 'emitting', x); 36 | * } 37 | * var emitter = { 38 | * name: 'Test emitter', 39 | * emit: emit, 40 | * emitAsync: function () { 41 | * var self = this; 42 | * var args = arguments; 43 | * setImmediate(function () { 44 | * emit.apply(self, args); 45 | * }); 46 | * } 47 | * }; 48 | * ``` 49 | * 50 | * @param {function} fn 51 | * @returns {function} new function which, when invoked, executes original `fn` 52 | * asynchronously (via setImmediate). Preserves context and arguments, but 53 | * return value is ignored. 54 | */ 55 | module.exports.makeAsync = function (fn) { 56 | var apply = Function.prototype.apply.bind(fn); 57 | return function () { 58 | setImmediate(apply, this, arguments); 59 | }; 60 | }; 61 | 62 | /** 63 | * @private 64 | */ 65 | var enqueued = function (atTop, fn) { 66 | return function enqeuedMethod () { 67 | var self = this; 68 | var args = arguments; 69 | var method = atTop ? 'enqueueAtTop' : 'enqueue'; 70 | return self._promiseQueue[method](function (resolve, reject) { 71 | var result = fn.apply(self, args); 72 | Promise.resolve(result).then(resolve, reject); 73 | }); 74 | }; 75 | }; 76 | 77 | /** 78 | * Wraps provided function into 79 | * {@link module:promiseQueue~PromiseQueue#enqueue}. 80 | * 81 | * It should be used only for methods and it expects that the class has 82 | * `_promiseQueue` property. 83 | * 84 | * Example: 85 | * ``` 86 | * function WifiListener() { 87 | * this._promiseQueue = new PromiseQueue(); 88 | * this._isStarted = false; 89 | * } 90 | * 91 | * WifiListener.prototype.start = enqueuedMethod(function () { 92 | * return this.performAsyncLogic().then(function () { 93 | * this._isStarted = true 94 | * }.bind(this)); 95 | * }); 96 | * ``` 97 | * 98 | * @method 99 | * @static 100 | * @param {function} fn - function to wrap. MUST be either synchronous or return 101 | * a Promise 102 | * @returns {Promise} 103 | */ 104 | module.exports.enqueuedMethod = enqueued.bind(null, false); 105 | 106 | /** 107 | * The same as [enqueuedMethod]{@link module:utils/common.enqueuedMethod} but 108 | * uses [enqueueAtTop]{@link module:promiseQueue~PromiseQueue#enqueueAtTop} 109 | * instead. 110 | * 111 | * @method 112 | * @static 113 | * @param {function} fn - function to wrap. MUST be either synchronous or return 114 | * a Promise 115 | * @returns {Promise} 116 | */ 117 | module.exports.enqueuedAtTopMethod = enqueued.bind(null, true); 118 | -------------------------------------------------------------------------------- /thali/NextGeneration/notification/thaliPskMapCache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var thaliConfig = require('../thaliConfig'); 3 | 4 | /** @module thaliPskMapCache */ 5 | 6 | /** 7 | * @classdesc A class that stores PskMaps. When new entries are added into the 8 | * cache object or existing entries are fetched from it we always check if 9 | * there are expired items. If expired items are found they will be removed. 10 | * 11 | * @public 12 | * @constructor 13 | * @param {number} millisecondsUntilExpiration The number of milliseconds into 14 | * the future after which the dictionary should expire. 15 | */ 16 | function ThaliPskMapCache(millisecondsUntilExpiration) { 17 | this._queue = []; 18 | this._millisecondsUntilExpiration = millisecondsUntilExpiration; 19 | } 20 | 21 | // jscs:disable maximumLineLength 22 | /** 23 | * Stores the dictionary into the cache. And removes expired items. 24 | * 25 | * @public 26 | * @param {module:thaliNotificationBeacons~beaconStreamAndSecretDictionary} dictionary 27 | */ 28 | // jscs:enable maximumLineLength 29 | ThaliPskMapCache.prototype.push = function (dictionary) { 30 | this.clean(true); 31 | var item = { keySecret: dictionary, expiration : Date.now() + 32 | this._millisecondsUntilExpiration - 200}; 33 | this._queue.push(item); 34 | }; 35 | 36 | /** 37 | * Returns the secret key associated with the ID or null if there is no match. 38 | * And removes expired items. 39 | * 40 | * @public 41 | * @param {string} id 42 | * @returns {?Buffer} The secret key associated with the ID or null if there 43 | * is no match. 44 | */ 45 | ThaliPskMapCache.prototype.getSecret = function (id) { 46 | this.clean(false); 47 | for (var i = this._queue.length - 1 ; i >= 0 ; i--) { 48 | if (this._queue[i].keySecret[id] && 49 | this._queue[i].keySecret[id].pskSecret) { 50 | return this._queue[i].keySecret[id].pskSecret; 51 | } 52 | } 53 | return null; 54 | }; 55 | 56 | /** 57 | * Returns the public key associated with the ID or null if there is no match. 58 | * And removes expired items. 59 | * 60 | * @public 61 | * @param {string} id 62 | * @returns {?Buffer} The public key associated with the ID or null if there 63 | * is no match. 64 | */ 65 | ThaliPskMapCache.prototype.getPublic = function (id) { 66 | this.clean(false); 67 | for (var i = this._queue.length - 1 ; i >= 0 ; i--) { 68 | if (this._queue[i].keySecret[id] && 69 | this._queue[i].keySecret[id].publicKey) { 70 | return this._queue[i].keySecret[id].publicKey; 71 | } 72 | } 73 | return null; 74 | }; 75 | 76 | /** 77 | * Cleans expired items from the dictionary. 78 | * 79 | * @public 80 | * @param {?boolean} forceRemove Forces to remove at least one item from the 81 | * dictionary, if it is full. 82 | */ 83 | ThaliPskMapCache.prototype.clean = function (forceRemove) { 84 | var clearCount = 0; 85 | var now = Date.now(); 86 | 87 | for ( var i = 0 ; i < this._queue.length ; i++) { 88 | if (this._queue[i].expiration < now ) { 89 | clearCount = i+1; 90 | } else { 91 | break; 92 | } 93 | } 94 | 95 | if (clearCount > 0 ) { 96 | this._queue.splice(0, clearCount); 97 | return; 98 | } 99 | 100 | if (forceRemove && this._queue.length >= 101 | thaliConfig.MAX_NOTIFICATIONSERVER_PSK_MAP_CACHE_SIZE) { 102 | this._queue.shift(); 103 | } 104 | }; 105 | 106 | module.exports = ThaliPskMapCache; 107 | -------------------------------------------------------------------------------- /thali/NextGeneration/utils/pouchDBCheckpointsPlugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('lie'); 4 | var logger = require('../../ThaliLogger')('pouchDBCheckpointsPlugin'); 5 | var makeAsync = require('./common').makeAsync; 6 | 7 | module.exports.onCheckpointReached = function (handler) { 8 | var db = this; 9 | var plugin = db.__checkpointPlugin; 10 | 11 | if (!plugin) { 12 | plugin = db.__checkpointPlugin = new CheckpointPlugin(db); 13 | } 14 | 15 | plugin.registerHandler(handler); 16 | }; 17 | 18 | var DEFAULT_DELAY = 200; 19 | 20 | var CheckpointPlugin = function (_db) { 21 | // Private data 22 | var db = _db; 23 | var checkpoint = db.__opts.checkpoint; 24 | var delay = db.__opts.checkpointDelay || DEFAULT_DELAY; 25 | var handlers = []; 26 | var destroyed = false; 27 | 28 | // Private methods 29 | function checkDBSize () { 30 | if (destroyed) { 31 | logger.warn('Couldn\'t check db size after destroy'); 32 | return Promise.resolve(); 33 | } 34 | 35 | return db.info() 36 | .then(function (response) { 37 | if (!response) return; 38 | 39 | // We want to call 'getDiskSize' directly without ignoring errors. 40 | // https://github.com/pouchdb/pouchdb-size/issues/22 41 | return db.getDiskSize() 42 | .then(function (diskSize) { 43 | // Handlers should be called only once 44 | // after the first reaching of a checkpoint. 45 | if (diskSize >= checkpoint) { 46 | handlers.forEach(function (handler) { 47 | handler(checkpoint); 48 | }); 49 | } 50 | }); 51 | }) 52 | .catch(function (error) { 53 | logger.error( 54 | 'Error while fetching db info: \'%s\', stack: \'%s\'', 55 | String(error), error.stack 56 | ); 57 | db.emit('error', error); 58 | }); 59 | } 60 | 61 | // Public constructor 62 | function CheckpointPlugin () { 63 | if (!db.getDiskSize) { 64 | throw new Error( 65 | 'This plugin depends on pouchdb-size plugin. ' + 66 | 'Please add pouchdb-size plugin befor this one.' 67 | ); 68 | } 69 | 70 | var changes = db.changes({ 71 | live: true, 72 | since: 'now' 73 | }) 74 | .on('error', function (error) { 75 | logger.error( 76 | 'Error while fetching db changes: \'%s\', stack: \'%s\'', 77 | String(error), error.stack 78 | ); 79 | changes.cancel(); 80 | db.emit('error', error); 81 | }) 82 | .on('change', executeOnce(checkDBSize, delay)); 83 | 84 | db.on('destroyed', function () { 85 | destroyed = true; 86 | changes.cancel(); 87 | }); 88 | } 89 | 90 | // Public methods 91 | CheckpointPlugin.prototype.registerHandler = function (handler) { 92 | // It might be better for performance 93 | // to execute handlers asynchronously. 94 | handlers.push(makeAsync(handler)); 95 | }; 96 | 97 | return new CheckpointPlugin(); 98 | }; 99 | 100 | var executeOnce = function (fn, delay) { 101 | var timeout = null; 102 | return function () { 103 | var self = this; 104 | var args = arguments; 105 | if (!timeout) { 106 | timeout = setTimeout(function () { 107 | fn.apply(self, args); 108 | clearTimeout(timeout); 109 | timeout = null; 110 | }, delay); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/IdentityExchange/testLiveIdentityExchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tape = require('../lib/thaliTape'); 4 | var IdentityExchange = require('thali/identityExchange/identityexchange'); 5 | var identityExchangeTestUtils = require('./identityExchangeTestUtils'); 6 | var ThaliReplicationManager = require('thali/thalireplicationmanager'); 7 | var ThaliEmitter = require('thali/thaliemitter'); 8 | var testUtils = require('../lib/testUtils'); 9 | 10 | var thaliApp = null; 11 | var thaliServer = null; 12 | 13 | function setUpServer() { 14 | return identityExchangeTestUtils.createThaliAppServer() 15 | .then(function (appAndServer) { 16 | thaliApp = appAndServer.app; 17 | thaliServer = appAndServer.server; 18 | }).catch(function (err) { 19 | throw err; 20 | }); 21 | } 22 | 23 | var test = tape({ 24 | setup: function (t) { 25 | setUpServer().then(function () {t.end(); }); 26 | }, 27 | teardown: function (t) { 28 | if (thaliServer) { 29 | thaliServer.close(); 30 | } 31 | thaliServer = null; 32 | thaliApp = null; 33 | t.end(); 34 | } 35 | }); 36 | 37 | 38 | test('Now do an identity Exchange with the real live system!', function (t) { 39 | if (!jxcore.utils.OSInfo().isMobile) { 40 | t.pass('Skipping test because we aren\'t running on a mobile platform'); 41 | t.end(); 42 | return; 43 | } 44 | 45 | var dbName = 'thali'; 46 | var LevelDownPouchDB = testUtils.LevelDownPouchDB(); 47 | var thaliReplicationManager = 48 | new ThaliReplicationManager(new LevelDownPouchDB(dbName)); 49 | var identityExchange = new IdentityExchange(thaliApp, 50 | thaliServer.address().port, thaliReplicationManager, 51 | dbName); 52 | thaliReplicationManager._emitter.on( 53 | ThaliEmitter.events.PEER_AVAILABILITY_CHANGED, function (peer) { 54 | t.pass('We found a peer - ' + JSON.stringify(peer)); 55 | }); 56 | var peerIdentityExchangeHandler = function (peer) { 57 | t.pass('We got a peer to do identity exchange with! - ' + 58 | JSON.stringify(peer)); 59 | if (peer.peerAvailable) { 60 | identityExchange.removeListener( 61 | IdentityExchange.Events.PeerIdentityExchange, 62 | peerIdentityExchangeHandler); 63 | t.pass('We are going to try and do an identity exchange with the peer'); 64 | identityExchange.executeIdentityExchange(peer.peerIdentifier, 65 | peer.peerName, function (err, code) { 66 | t.notOk(err, 'Did we get an error on executeIdentityExchange?'); 67 | identityExchangeTestUtils.checkCode(t, code); 68 | // The side with the larger hash can end up quiting fast enough to 69 | // kill the connection before the response goes back causing a hang on 70 | // the side with the smaller hash. So we put in a delay to make sure 71 | // everything gets through. 72 | setTimeout(function () { 73 | identityExchange.stopExecutingIdentityExchange(); 74 | identityExchange.stopIdentityExchange(function (err) { 75 | t.notOk(err, 'Did we get a problem in calling stop Identity ' + 76 | 'Exchange?'); 77 | t.end(); 78 | }); 79 | }, 200); 80 | }); 81 | } 82 | }; 83 | identityExchange.on(IdentityExchange.Events.PeerIdentityExchange, 84 | peerIdentityExchangeHandler); 85 | identityExchange.startIdentityExchange('Sreejumon', function (err) { 86 | t.notOk(err, 'Did we successfully get a callback from start?'); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/www/jxcore/meta_tests/testTestSendData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var originalMobile = typeof Mobile === 'undefined' ? undefined : Mobile; 4 | var SendDataConnector = require('../perf_tests/SendDataConnector.js'); 5 | var SendDataTCPServer = require('../perf_tests/SendDataTCPServer.js'); 6 | var TestSendData = require('../perf_tests/testSendData.js'); 7 | var tape = require('../lib/thaliTape'); 8 | 9 | var testPort = 8889; 10 | 11 | var test = tape({ 12 | setup: function(t) { 13 | global.Mobile = function (key) { 14 | return { 15 | 'callNative': function () { 16 | var cb = arguments[arguments.length - 1]; 17 | 18 | if (typeof cb !== 'function') { 19 | throw new Error('The last argument of the Mobile callNative ' + 20 | 'method must be a callback'); 21 | } 22 | 23 | if (key === 'Connect') { 24 | setTimeout(function () { 25 | cb(null, testPort); 26 | }, 100); 27 | } else { 28 | setTimeout(function () { 29 | cb(null); 30 | }, 100); 31 | } 32 | }, 33 | 'registerToNative': function () {} 34 | }; 35 | }; 36 | t.end(); 37 | }, 38 | teardown: function(t) { 39 | global.Mobile = originalMobile; 40 | t.end(); 41 | } 42 | }); 43 | 44 | var somePeer = { 45 | 'peerIdentifier': 'some-peer-identifier' 46 | }; 47 | 48 | test('connector should fail if server not running', function (t) { 49 | var retryCount = 0; // Make it 0 so that we fail after first attempt 50 | var sendDataConnector = new SendDataConnector(1, 0, 0, retryCount, 0); 51 | 52 | sendDataConnector.on('done', function (result) { 53 | t.ok(result, 'received a result to the done event'); 54 | sendDataConnector.Stop(function () { 55 | t.end(); 56 | }); 57 | }); 58 | sendDataConnector.Start(somePeer); 59 | }); 60 | 61 | test('connector should be able to send data to a running server', function (t) { 62 | var retryTimeout = 200; 63 | var dataAmount = 1000000; // amount in bytes 64 | var sendDataConnector = new SendDataConnector(1, dataAmount, retryTimeout, 0, 65 | 0); 66 | 67 | var sendDataTCPServer = new SendDataTCPServer(testPort); 68 | 69 | sendDataConnector.on('done', function (result) { 70 | t.ok(result, 'received a result to the done event'); 71 | sendDataTCPServer.stopServer(function () { 72 | t.end(); 73 | }); 74 | }); 75 | sendDataConnector.Start(somePeer); 76 | }); 77 | 78 | var numberOfPeers = 5; 79 | 80 | test('should run test with ' + numberOfPeers + ' peers', function (t) { 81 | var testData = { 82 | 'timeout': 1500000, 83 | 'rounds': 1, 84 | 'dataTimeout': 10000, 85 | 'dataAmount': 1000000, 86 | 'conReTryTimeout': 50, 87 | 'conReTryCount': 5, 88 | 'peerCount' : numberOfPeers 89 | }; 90 | 91 | var testPeerList = []; 92 | for (var i = 0; i < numberOfPeers; i++) { 93 | testPeerList.push({ 94 | 'address': 'device-address-' + i 95 | }); 96 | } 97 | var testSendData = new TestSendData(testData, 'device-identifier-me', testPeerList); 98 | testSendData.start(testPort); 99 | 100 | testSendData.on('done', function (resultString) { 101 | t.ok(resultString, 'received a result to the done event'); 102 | var resultData = JSON.parse(resultString); 103 | t.equal(resultData.sendList.length, numberOfPeers); 104 | testSendData.stop(false); 105 | t.end(); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/TestThaliMobileNative/Message.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Promise = require('lie'); 5 | 6 | var objectKeysEquals = require('thali/validations').objectKeysEquals; 7 | 8 | var logger = require('../../../lib/testLogger')('Message'); 9 | 10 | 11 | function Message (uuid, code, bulkData) { 12 | assert(!isNaN(code), 'code should be a number'); 13 | 14 | this.uuid = uuid; 15 | this.code = code; 16 | this.bulkData = bulkData || Message.bulkData; 17 | } 18 | 19 | var bulkData = new Buffer(100000); 20 | bulkData.fill(1); 21 | Message.bulkData = bulkData; 22 | 23 | Message.codes = { 24 | // The sender is not in the same generation as the receiver 25 | WRONG_GEN: 0, 26 | // The sender is not in the participants list for the receiver 27 | WRONG_TEST: 1, 28 | // Everything matched 29 | SUCCESS: 2, 30 | // We got an old advertisement for ourselves! 31 | WRONG_ME: 3, 32 | // A peer on our list gave us bad syntax, no hope of test passing 33 | WRONG_SYNTAX: 4 34 | }; 35 | 36 | Message.prototype.toString = function () { 37 | return JSON.stringify({ 38 | uuid: this.uuid, 39 | code: this.code, 40 | bulkData: this.bulkData.toString() 41 | }); 42 | } 43 | 44 | Message.prototype.writeTo = function (socket) { 45 | var self = this; 46 | 47 | return new Promise(function (resolve, reject) { 48 | var str = self.toString(); 49 | 50 | // We will send message's length in first 4 bytes. 51 | var length = str.length; 52 | var data = new Buffer(4); 53 | data.writeUInt32BE(length, 0); 54 | socket.write(data); 55 | 56 | // Than we will send the message itself. 57 | socket.write(str, resolve); 58 | }) 59 | } 60 | 61 | Message.fromString = function (str) { 62 | var data = JSON.parse(str); 63 | assert( 64 | objectKeysEquals(data, ['uuid', 'code', 'bulkData']), 65 | 'message should include uuid, code and bulkData and nothing else' 66 | ); 67 | assert(!isNaN(data.code), 'code should be a number'); 68 | data.code = parseInt(data.code, 10); 69 | return new Message(data.uuid, data.code, data.bulkData); 70 | } 71 | 72 | Message.read = function (socket) { 73 | return new Promise(function (resolve, reject) { 74 | var targetLength = null; 75 | var currentData = new Buffer(0); 76 | 77 | function handler (data) { 78 | currentData = Buffer.concat([currentData, data]); 79 | 80 | // We will read message length from the first 4 bytes. 81 | if (!targetLength) { 82 | if (currentData.length < 4) { 83 | // We need more data. 84 | socket.once('data', handler); 85 | return; 86 | } else { 87 | targetLength = currentData.readUInt32BE(0); 88 | assert(!isNaN(targetLength), 'target length should exist'); 89 | targetLength += 4; 90 | } 91 | } 92 | if (currentData.length === targetLength) { 93 | resolve( 94 | Message.fromString( 95 | currentData.toString('utf8', 4) 96 | ) 97 | ); 98 | } else if (currentData.length < targetLength) { 99 | // We need more data. 100 | socket.once('data', handler); 101 | } else { 102 | reject(new Error( 103 | 'data is too long, length is: ' + currentData.length + 104 | ', expected length: ' + targetLength 105 | )); 106 | } 107 | } 108 | socket.once('data', handler); 109 | }); 110 | } 111 | 112 | module.exports = Message; 113 | -------------------------------------------------------------------------------- /thali/NextGeneration/makeIntoCloseAllServer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Promise = require('lie'); 5 | var logger = require('../ThaliLogger')('makeIntoCloseAllServer'); 6 | 7 | /** @module makeIntoCloseAllServer */ 8 | 9 | /** 10 | * @public 11 | * @callback thunk 12 | */ 13 | 14 | /** 15 | * Takes any of the NET server object types (such as HTTP, HTTPS and NET itself 16 | * which we use for TCP) and calls their createServer method with the submitted 17 | * options and connectionListener. It's job is to add a closeAll method that 18 | * if called will destroy any outstanding connections to the server as well 19 | * as close the server itself. 20 | * 21 | * @public 22 | * @param {net.Server} server Server object 23 | * @param {boolean} [eatNotRunning] Will consume a not running error when 24 | * calling one of our close methods rather than throwing it. 25 | * @returns {net.Server} Wrapper making the server support close all 26 | */ 27 | function makeIntoCloseAllServer(server, eatNotRunning) { 28 | var connections = []; 29 | 30 | var _connectionHandler = function (socket) { 31 | // Add to the list of connections. 32 | connections.push(socket); 33 | // Remove from list of connections in case 34 | // socket is closed. 35 | socket.once('close', function () { 36 | var index = connections.indexOf(socket); 37 | if (index === -1) { 38 | assert('socket not found from the list of connections'); 39 | } 40 | connections.splice(index, 1); 41 | }); 42 | } 43 | .bind(this); 44 | server.on('connection', _connectionHandler); 45 | 46 | /** 47 | * Closes the server and then closes all incoming connections to the server. 48 | * 49 | * @param {thunk} [callback] Callback 50 | */ 51 | server.closeAll = function (callback) { 52 | logger.debug('closeAll called on server'); 53 | var forceCallback = false; 54 | // By closing the server first we prevent any new incoming connections 55 | // to the server. 56 | // Also note that the callback won't be called until all the connections 57 | // are destroyed because the destroy calls are synchronous. 58 | try { 59 | server.close(callback); 60 | } catch (err){ 61 | if (!eatNotRunning || !(err instanceof Error) || 62 | (err && err.message !== 'Not running')) { 63 | throw err; 64 | } 65 | forceCallback = true; 66 | } 67 | 68 | connections.forEach(function (connection) { 69 | connection.destroy(); 70 | }); 71 | 72 | if (forceCallback && callback) { 73 | callback(); 74 | } 75 | }; 76 | 77 | /** 78 | * Same as closeAll but returns a promise. 79 | * 80 | * @returns {Promise} 81 | */ 82 | server.closeAllPromise = function () { 83 | var self = this; 84 | return new Promise(function (resolve, reject) { 85 | self.closeAll(function (err) { 86 | if (err) { 87 | return reject(err); 88 | } 89 | resolve(); 90 | }); 91 | }); 92 | }; 93 | 94 | var _removeAllListeners = server.removeAllListeners; 95 | server.removeAllListeners = function (eventName) { 96 | var result = _removeAllListeners.apply(this, arguments); 97 | if (eventName === 'connection') { 98 | // We can protect out connection handler 99 | server.on('connection', _connectionHandler); 100 | } 101 | return result; 102 | } 103 | 104 | return server; 105 | } 106 | 107 | module.exports = makeIntoCloseAllServer; 108 | -------------------------------------------------------------------------------- /src/android/test/io/jxcore/node/OutgoingSocketThreadMock.java: -------------------------------------------------------------------------------- 1 | package io.jxcore.node; 2 | 3 | import android.bluetooth.BluetoothSocket; 4 | import android.util.Log; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.net.ServerSocket; 10 | import org.thaliproject.p2p.btconnectorlib.PeerProperties; 11 | 12 | public class OutgoingSocketThreadMock extends OutgoingSocketThread { 13 | Long threadId = 1234L; 14 | int port; 15 | boolean closeCalled = false; 16 | InputStream tempInputStream = null; 17 | OutputStream tempOutputStream = null; 18 | boolean localStreamsCreatedSuccessfully = false; 19 | ServerSocket mServerSocket = null; 20 | int mListeningOnPortNumber = ConnectionHelper.NO_PORT_NUMBER; 21 | 22 | public OutgoingSocketThreadMock(BluetoothSocket bluetoothSocket, Listener listener, 23 | InputStream inputStream, OutputStream outputStream) 24 | throws IOException { 25 | super(bluetoothSocket, listener, inputStream, outputStream); 26 | } 27 | 28 | public void setPort(int _port){ 29 | port = _port; 30 | } 31 | 32 | @Override 33 | public void close() { 34 | closeCalled = true; 35 | } 36 | 37 | @Override 38 | public long getId() { 39 | return threadId; 40 | } 41 | 42 | @Override 43 | public void run() { 44 | mIsClosing = false; 45 | 46 | try { 47 | mServerSocket = new ServerSocket(port); 48 | Log.d(mTag, "Server socket local port: " + mServerSocket.getLocalPort()); 49 | } catch (IOException e) { 50 | Log.e(mTag, "Failed to create a server socket instance: " + e.getMessage(), e); 51 | mServerSocket = null; 52 | mListener.onDisconnected(this, "Failed to create a server socket instance: " + 53 | e.getMessage()); 54 | } 55 | 56 | if (mServerSocket != null) { 57 | try { 58 | Log.i(mTag, "Now accepting connections..."); 59 | 60 | if (mListener != null) { 61 | mListeningOnPortNumber = mServerSocket.getLocalPort(); 62 | mListener.onListeningForIncomingConnections(mListeningOnPortNumber); 63 | } 64 | 65 | mLocalhostSocket = mServerSocket.accept(); // Blocking call 66 | 67 | Log.i(mTag, "Incoming data from address: " + getLocalHostAddressAsString() 68 | + ", port: " + mServerSocket.getLocalPort()); 69 | 70 | tempInputStream = mLocalhostSocket.getInputStream(); 71 | tempOutputStream = mLocalhostSocket.getOutputStream(); 72 | localStreamsCreatedSuccessfully = true; 73 | } catch (IOException e) { 74 | if (!mIsClosing) { 75 | String errorMessage = "Failed to create local streams: " + e.getMessage(); 76 | Log.e(mTag, errorMessage, e); 77 | mListener.onDisconnected(this, errorMessage); 78 | } 79 | } 80 | 81 | if (localStreamsCreatedSuccessfully) { 82 | Log.d(mTag, "Setting local streams and starting stream copying threads..."); 83 | mLocalInputStream = tempInputStream; 84 | mLocalOutputStream = tempOutputStream; 85 | 86 | startStreamCopyingThreads(new ConnectionData( 87 | new PeerProperties(PeerProperties.BLUETOOTH_MAC_ADDRESS_UNKNOWN), false)); 88 | } 89 | } 90 | } 91 | } 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/android/java/io/jxcore/node/ListenerOrIncomingConnection.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2016 Microsoft Corporation. This software is licensed under the MIT License. 2 | * See the license file delivered with this project for further information. 3 | */ 4 | package io.jxcore.node; 5 | 6 | import android.util.Log; 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | /** 11 | * Utility class for JXcoreThaliCallback. 12 | */ 13 | class ListenerOrIncomingConnection { 14 | private static final String TAG = ListenerOrIncomingConnection.class.getSimpleName(); 15 | private int mListeningOnPortNumber = 0; 16 | private int mClientPortNumber = 0; 17 | private int mServerPortNumber = 0; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param listeningOnPortNumber The port on which the native layer is listening on 127.0.0.1 for 23 | * an incoming TCP/IP connection that the native layer will then 24 | * relay to the remote peer. 25 | * @param clientPortNumber clientPort The port that the native layer's TCP/IP client uses to 26 | * connect to the `portNumber` submitted by the Thali application. 27 | * @param serverPortNumber The port that the native layer's TCP/IP client connected to. The 28 | * reason we include it here is because there is a potential race 29 | * condition where between the time we created the response to the 30 | * connect request and when it was actually sent to Node.js in theory we 31 | * could have received a stop and start that switched us to a different 32 | * `portNumber`. So by including `serverPort` we can catch those race 33 | * conditions. 34 | */ 35 | public ListenerOrIncomingConnection(int listeningOnPortNumber, int clientPortNumber, int serverPortNumber) { 36 | mListeningOnPortNumber = listeningOnPortNumber; 37 | mClientPortNumber = clientPortNumber; 38 | mServerPortNumber = serverPortNumber; 39 | } 40 | 41 | /** 42 | * Constructor. 43 | */ 44 | public ListenerOrIncomingConnection() { 45 | mListeningOnPortNumber = ConnectionHelper.NO_PORT_NUMBER; 46 | mClientPortNumber = ConnectionHelper.NO_PORT_NUMBER; 47 | mServerPortNumber = ConnectionHelper.NO_PORT_NUMBER; 48 | } 49 | 50 | public int getListeningOnPortNumber() { 51 | return mListeningOnPortNumber; 52 | } 53 | 54 | public void setListeningOnPortNumber(int listeningOnPortNumber) { 55 | mListeningOnPortNumber = listeningOnPortNumber; 56 | } 57 | 58 | public JSONObject toJsonObject() { 59 | try { 60 | JSONObject jsonObject = new JSONObject(); 61 | jsonObject.put(JXcoreExtension.CALLBACK_VALUE_LISTENING_ON_PORT_NUMBER, mListeningOnPortNumber); 62 | jsonObject.put(JXcoreExtension.CALLBACK_VALUE_CLIENT_PORT_NUMBER, mClientPortNumber); 63 | jsonObject.put(JXcoreExtension.CALLBACK_VALUE_SERVER_PORT_NUMBER, mServerPortNumber); 64 | return jsonObject; 65 | } catch (JSONException e) { 66 | Log.e(TAG, "toJsonObject: Failed to populate the JSON object: " + e.getMessage(), e); 67 | } 68 | 69 | return null; 70 | } 71 | 72 | public String toString() { 73 | JSONObject jsonObject = toJsonObject(); 74 | 75 | if (jsonObject != null) { 76 | return jsonObject.toString(); 77 | } 78 | 79 | return null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/www/jxcore/CITestMode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* CI Test mode - script which purpose is to test if CI enviroment is working 4 | * properly by running one simple test for android and ios native layer and 5 | * node layer. 6 | */ 7 | 8 | const fs = require('fs-extra-promise'); 9 | 10 | var setUpFunctions = { 11 | 12 | updateUnitTestConfig: () => { 13 | const locationOfUnitTestConfig = '../../TestServer/UnitTestConfig.js'; 14 | 15 | let originalContent = fs.readFileSync(locationOfUnitTestConfig); 16 | let newContent = originalContent.toString().replace(/numDevices: -?[0-9]/g, 'numDevices: -1'); 17 | 18 | if (originalContent === newContent) { 19 | throw new Error('Replace function with regex didn\'t worked properly!'); 20 | } 21 | 22 | fs.writeFileSync(locationOfUnitTestConfig, newContent); 23 | }, 24 | 25 | updateThaliTestSuiteFunction: () => { 26 | const locationOfAndroidBeforeCompile = '../../../scripts/android/before_compile.js'; 27 | 28 | let originalContent = fs.readFileSync(locationOfAndroidBeforeCompile); 29 | let newContent = originalContent.toString().replace('Test.java', 'CITest'); 30 | 31 | fs.writeFileSync(locationOfAndroidBeforeCompile, newContent, 'utf-8'); 32 | }, 33 | 34 | copyCINativeTestClass: () => { 35 | const path = '../../../scripts/android/before_compile.js'; 36 | 37 | let originalContent = fs.readFileSync(path); 38 | let oldFunc = 'var i, testClassName;'; 39 | let newFunc = oldFunc + '\nfs.copySync(appRoot + \'/plugins/org.thaliproject.p2p/src/android/test/io/jxcore/node/CITestClass.java\', appRoot + \'/platforms/android/src/io/jxcore/node/CITestClass.java\');'; 40 | let newContent = originalContent.toString().replace(oldFunc, newFunc); 41 | 42 | fs.writeFileSync(path, newContent); 43 | }, 44 | 45 | updateRunTestsToRunOnlyOneNodeTest: () => { 46 | const locationOfRunTests = './runTests.js'; 47 | 48 | let originalContent = fs.readFileSync(locationOfRunTests); 49 | let newContent = originalContent.toString().replace('fileName.indexOf(\'test\') === 0)', 'fileName.indexOf(\'CITest\') === 0)'); 50 | 51 | fs.writeFileSync(locationOfRunTests, newContent); 52 | }, 53 | 54 | copyCINodeTestClass: () => { 55 | fs.renameSync('bv_tests/disabled/CITestClass.js', 'bv_tests/CITestClass.js'); 56 | }, 57 | 58 | emptyAlliOSTestFilesButOne: (pathParam) => { 59 | let path; 60 | 61 | path = pathParam || '../../../lib/ios/ThaliCore/ThaliCoreTests'; 62 | 63 | if (path === '../../../lib/ios/ThaliCore/ThaliCoreTests' && !fs.existsSync(path + '/SimpleTestCase.swift')) { 64 | throw new Error('SimpleTestCase test file was not found!'); 65 | } 66 | 67 | const filesArray = fs.readdirSync(path); 68 | let currentFilePath, i; 69 | 70 | for (i = 0; i < filesArray.length; i++) { 71 | if (filesArray[i].indexOf('SimpleTestCase') === -1) { 72 | currentFilePath = path + '/' + filesArray[i].toString(); 73 | if (!fs.lstatSync(currentFilePath).isDirectory()) { 74 | fs.writeFileSync(currentFilePath, 'import Foundation\n'); 75 | } else { 76 | setUpFunctions.emptyAlliOSTestFilesButOne(currentFilePath); 77 | } 78 | } 79 | } 80 | }, 81 | }; 82 | 83 | function runFunctionAndCheckFailures(func, errStr) { 84 | try { 85 | func(); 86 | } catch (e) { 87 | console.log(e); 88 | console.log(errStr); 89 | process.exit(-1); 90 | } 91 | } 92 | 93 | for (let name in setUpFunctions) { 94 | if (setUpFunctions.hasOwnProperty(name)) { 95 | runFunctionAndCheckFailures(setUpFunctions[name], name); 96 | } 97 | } 98 | 99 | process.exit(0); 100 | -------------------------------------------------------------------------------- /test/www/jxcore/runTests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var randomString = require('randomstring'); 5 | var testLoader = require('./lib/testLoader'); 6 | var config = require('./config'); 7 | 8 | // Before including anything serious from thali we want to ensure 9 | // that we have SSDP_NT env defined. 10 | if (!process.env.SSDP_NT) { 11 | // We want to provide a new random value. 12 | process.env.SSDP_NT = randomString.generate({ 13 | length: 'http://www.thaliproject.org/ssdp'.length 14 | }); 15 | } 16 | // Override sinon timers 17 | // issue https://github.com/thaliproject/jxcore/issues/86 18 | var sinon = require('sinon'); 19 | sinon.config = { 20 | useFakeTimers: false 21 | }; 22 | 23 | var thaliTape = require('./lib/thaliTape'); 24 | var testUtils = require('./lib/testUtils'); 25 | var logger = require('./lib/testLogger')('runTests'); 26 | 27 | var platform = require('thali/NextGeneration/utils/platform'); 28 | 29 | var DEFAULT_PLATFORM = platform.names.ANDROID; 30 | var mockPlatform; 31 | 32 | var argv = require('minimist')(process.argv.slice(2), { 33 | string: ['platform', 'networkType'], 34 | default: { 35 | 'platform': DEFAULT_PLATFORM 36 | } 37 | }); 38 | 39 | // The global.Mobile object is replaced here after thaliTape 40 | // has been required so that thaliTape can pick up the right 41 | // test framework to be used. 42 | if (typeof Mobile === 'undefined') { 43 | mockPlatform = require('./lib/parsePlatformArg')() || DEFAULT_PLATFORM; 44 | global.Mobile = require('./lib/wifiBasedNativeMock.js')(mockPlatform); 45 | } else { 46 | mockPlatform = Mobile._platform; // mock may be created in UnitTest_app.js 47 | } 48 | 49 | var networkTypes = require('thali/NextGeneration/thaliMobile').networkTypes; 50 | 51 | if (argv.networkType) { 52 | var networkType = argv.networkType.toUpperCase(); 53 | switch (networkType) { 54 | case networkTypes.WIFI: 55 | case networkTypes.NATIVE: 56 | case networkTypes.BOTH: { 57 | global.NETWORK_TYPE = networkType; 58 | break; 59 | } 60 | default: { 61 | logger.warn( 62 | 'Unrecognized network type: ' + networkType + '. ' + 63 | 'Available network types: ' + [ 64 | networkTypes.WIFI, 65 | networkTypes.NATIVE, 66 | networkTypes.BOTH, 67 | ].join(', ') 68 | ); 69 | process.exit(1); 70 | } 71 | } 72 | } 73 | 74 | var currentPlatform = platform.name; 75 | // Our current platform can be 'darwin', 'linux', 'windows', etc. 76 | // Our 'thaliTape' expects all these platforms will be named as 'desktop'. 77 | if (!platform.isMobile) { 78 | currentPlatform = 'desktop'; 79 | } 80 | 81 | logger.info( 82 | 'Starting tests. ' + 83 | 'Network type: ' + global.NETWORK_TYPE + '. ' + 84 | 'Platform: ' + (mockPlatform || currentPlatform) 85 | ); 86 | 87 | var testsToRun = argv._[0] || 'bv_tests'; 88 | var testsPath = path.join(__dirname, testsToRun); 89 | testLoader.load(testsPath, config.preferredOrder); 90 | 91 | testUtils.hasRequiredHardware() 92 | .then(function (hasRequiredHardware) { 93 | 94 | return testUtils.enableRequiredHardware() 95 | .then(function (enableRequiredHardware) { 96 | 97 | return testUtils.getOSVersion() 98 | .then(function (version) { 99 | 100 | return thaliTape.begin( 101 | currentPlatform, version, 102 | hasRequiredHardware || enableRequiredHardware, 103 | global.nativeUTFailed 104 | ); 105 | }); 106 | }); 107 | }) 108 | .then(function () { 109 | logger.info('Finished'); 110 | process.exit(0); 111 | }) 112 | .catch(function (error) { 113 | logger.error(error.message + '\n' + error.stack); 114 | process.exit(1); 115 | }); 116 | -------------------------------------------------------------------------------- /thali/thalicryptomanager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var urlSafeBase64 = require('urlsafe-base64'); 7 | 8 | // The password is not secure because anyone who can get to the file can get 9 | // to the app and thus can get the password. The password is used here only to 10 | // satisfy the crypto/PKCS12 APIs. 11 | var password = 'password'; 12 | var certname = 'certname'; 13 | var country = 'country'; 14 | var organization = 'organization'; 15 | var pkcs12FileName = '/pkcs12.pfx'; 16 | var macName = 'SHA256'; 17 | var hashSizeInBytes = 16; 18 | 19 | var generateSlicedSHA256Hash = module.exports.generateSlicedSHA256Hash = 20 | function( 21 | bufferValueToHash, 22 | hashSizeInBytes) { 23 | 24 | var hash = crypto.createHash(macName); 25 | hash.update(bufferValueToHash); 26 | var fullSizeKeyHashBuffer = hash.digest(); 27 | var slicedBuffer = fullSizeKeyHashBuffer.slice(0, hashSizeInBytes); 28 | 29 | return urlSafeBase64.encode(slicedBuffer); 30 | }; 31 | 32 | /** 33 | * Reads the PKCS12 content from the given file if it exists. If not, 34 | * the PKCS12 content is generated, saved to a file and the content is 35 | * returned. 36 | * @param {String} fileNameWithPath the file which has the PKCS12 content. 37 | * @param {Function} cb the callback which returns an error or PKCS12 content. 38 | */ 39 | function getPKCS12Content(fileNameWithPath, cb) { 40 | fs.exists(fileNameWithPath, function (exists) { 41 | if(exists) { 42 | fs.readFile(fileNameWithPath, function (err, pkcs12Content) { 43 | if (err) { 44 | cb(err); 45 | return; 46 | } 47 | cb(null, pkcs12Content); 48 | }); 49 | } else { 50 | var pkcs12Content = crypto.pkcs12 51 | .createBundle(password, certname, country, organization); 52 | 53 | if (pkcs12Content.length <= 0) { 54 | return cb(new Error('failed to create pkcs12Content')); 55 | } 56 | 57 | fs.writeFile( 58 | fileNameWithPath, 59 | pkcs12Content, 60 | {flags: 'wx'}, 61 | function (err) { 62 | 63 | if (err) { 64 | return cb(err); 65 | } 66 | cb(null, pkcs12Content); 67 | }); 68 | } 69 | }); 70 | } 71 | 72 | /** 73 | * Checks if a PKCS12 file exists in a known location and if not present, it is 74 | * created. The public key is extracted from the PKCS12 content and it's SHA256 75 | * hash value is returned. 76 | * @param {Function} cb the callback which returns an error or the hash value. 77 | */ 78 | module.exports.getPublicKeyHash = function (cb) { 79 | Mobile.GetDocumentsPath(function (err, fileLocation) { 80 | if (err) { 81 | return cb(err); 82 | } 83 | 84 | var file = path.join(fileLocation, pkcs12FileName); 85 | getPKCS12Content(file, function(err, pkcs12Content) { 86 | if (err) { 87 | return cb(err); 88 | } 89 | 90 | try { 91 | var publicKey = crypto.pkcs12 92 | .extractPublicKey(password, pkcs12Content); 93 | if (!publicKey || publicKey.length <= 0) { 94 | return cb(new Error('extracted public key is invalid')); 95 | } 96 | var hash = generateSlicedSHA256Hash(publicKey, hashSizeInBytes); 97 | cb(null, hash); 98 | } catch(e) { 99 | return cb(new Error('error thrown by extractPublicKey() function')); 100 | } 101 | }); 102 | }); 103 | }; 104 | 105 | module.exports.getConfigValuesForTestingOnly = function() { 106 | return { 107 | password: password, 108 | certname: certname, 109 | country: country, 110 | organization: organization, 111 | pkcs12FileName: pkcs12FileName, 112 | hashSizeInBytes: hashSizeInBytes 113 | }; 114 | }; 115 | -------------------------------------------------------------------------------- /thali/thaliemitter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var EventEmitter = require('events').EventEmitter; 4 | var inherits = require('util').inherits; 5 | var validations = require('./validations'); 6 | 7 | function thrower(err) { 8 | if (err) { throw err; } 9 | } 10 | 11 | /** 12 | * Creates a new instance of the ThaliEmitter which is an EventEmitter to call the underlying native layer. 13 | */ 14 | function ThaliEmitter() { 15 | EventEmitter.call(this); 16 | this._init(); 17 | } 18 | 19 | inherits(ThaliEmitter, EventEmitter); 20 | 21 | ThaliEmitter.events = { 22 | PEER_AVAILABILITY_CHANGED: 'peerAvailabilityChanged', 23 | NETWORK_CHANGED: 'networkChanged', 24 | CONNECTION_ERROR: 'connectionError' 25 | }; 26 | 27 | ThaliEmitter.prototype._init = function () { 28 | var self = this; 29 | 30 | function registerMobilePeerEvent(eventName) { 31 | function emitEvent(eventName) { 32 | return function handler(arg) { 33 | self.emit(eventName, arg); 34 | }; 35 | } 36 | 37 | Mobile(eventName).registerToNative(emitEvent(eventName)); 38 | } 39 | 40 | Object.keys(ThaliEmitter.events) 41 | .forEach(function (key) { 42 | registerMobilePeerEvent(ThaliEmitter.events[key]); 43 | }); 44 | }; 45 | 46 | /** 47 | * Starts broadcasting with the given device name, port and a callback 48 | * @param {String} deviceName the device name to broadcast. 49 | * @param {Number} port the port number to broadcast. 50 | * @param {Function} cb the callback which returns an error if one has occurred. 51 | */ 52 | ThaliEmitter.prototype.startBroadcasting = function(deviceName, port, cb) { 53 | validations.ensureNonNullOrEmptyString(deviceName, 'deviceName'); 54 | validations.ensureValidPort(port); 55 | cb || (cb = thrower); 56 | 57 | Mobile('StartBroadcasting').callNative(deviceName, port, function (err) { 58 | if (err) { 59 | cb(new Error(err)); 60 | } else { 61 | cb(); 62 | } 63 | }); 64 | }; 65 | 66 | /** 67 | * Starts broadcasting the availability of the current device. 68 | * @param {Function} cb the callback which returns an error if one has occurred. 69 | */ 70 | ThaliEmitter.prototype.stopBroadcasting = function(cb) { 71 | cb || (cb = thrower); 72 | 73 | Mobile('StopBroadcasting').callNative(function (err) { 74 | if (err) { 75 | cb(new Error(err)); 76 | } else { 77 | cb(); 78 | } 79 | }); 80 | }; 81 | 82 | /** 83 | * Connects to the given peer by the given peer identifier. 84 | * @param {String} peerIdentifier the peer identifier of the device to connect to. 85 | * @param {Function} cb the callback which returns an error if one occurred and a port number used for synchronization. 86 | */ 87 | ThaliEmitter.prototype.connect = function (peerIdentifier, cb) { 88 | validations.ensureNonNullOrEmptyString(peerIdentifier, 'peerIdentifier'); 89 | validations.ensureIsFunction(cb); 90 | 91 | Mobile('Connect').callNative(peerIdentifier, function (err, port) { 92 | if (err) { 93 | cb(new Error(err)); 94 | } else { 95 | cb(null, port); 96 | } 97 | }); 98 | }; 99 | 100 | /** 101 | * Disconnects from the given peer by the peer identifier. Note if the peer has already been disconnected, no error should be thrown. 102 | * @param {String} peerIdentifier the peer identifier of the device to disconnect from. 103 | * @param {Function} cb the callback which returns an error if one occurred. 104 | */ 105 | ThaliEmitter.prototype.disconnect = function (peerIdentifier, cb) { 106 | validations.ensureNonNullOrEmptyString(peerIdentifier, 'peerIdentifier'); 107 | cb || (cb = thrower); 108 | 109 | Mobile('Disconnect').callNative(peerIdentifier, function (err) { 110 | if (err) { 111 | cb(new Error(err)); 112 | } else { 113 | cb(); 114 | } 115 | }); 116 | }; 117 | 118 | module.exports = ThaliEmitter; 119 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/testThaliManagerStopCoordinated.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var tape = require('../../lib/thaliTape'); 4 | if (!tape.coordinated) { 5 | return; 6 | } 7 | 8 | var testUtils = require('../../lib/testUtils.js'); 9 | 10 | var fs = require('fs-extra-promise'); 11 | var path = require('path'); 12 | var crypto = require('crypto'); 13 | var Promise = require('bluebird'); 14 | var PouchDB = require('pouchdb'); 15 | var ExpressPouchDB = require('express-pouchdb'); 16 | 17 | var sinon = require('sinon'); 18 | var proxyquire = require('proxyquire').noCallThru(); 19 | 20 | var salti = require('salti'); 21 | var PouchDBGenerator = require('thali/NextGeneration/utils/pouchDBGenerator'); 22 | var thaliConfig = require('thali/NextGeneration/thaliConfig'); 23 | var ThaliManager = require('thali/NextGeneration/thaliManager'); 24 | var ThaliPeerPoolDefault = 25 | require('thali/NextGeneration/thaliPeerPool/thaliPeerPoolDefault'); 26 | 27 | // Public base64 key for local device should be passed 28 | // to the tape 'setup' as 'tape.data'. 29 | // This is required for tape.coordinated server to generate participants. 30 | var ecdhForLocalDevice = crypto.createECDH(thaliConfig.BEACON_CURVE); 31 | var publicKeyForLocalDevice = ecdhForLocalDevice.generateKeys(); 32 | var publicBase64KeyForLocalDevice = ecdhForLocalDevice.getPublicKey('base64'); 33 | 34 | // PouchDB name should be the same between peers. 35 | var DB_NAME = 'ThaliManagerCoordinated'; 36 | 37 | PouchDB = testUtils.getLevelDownPouchDb(); 38 | 39 | var thaliManager; 40 | 41 | var test = tape({ 42 | setup: function (t) { 43 | t.data = publicKeyForLocalDevice.toJSON(); 44 | t.end(); 45 | }, 46 | teardown: function (t) { 47 | thaliManager.stop() 48 | .then(function () { 49 | t.end(); 50 | }); 51 | } 52 | }); 53 | 54 | test('test uncaught exception', function (t) { 55 | var spySalti; 56 | var allRequestsStarted = new Promise(function (resolve) { 57 | spySalti = sinon.spy(function () { 58 | var saltiFilter = salti.apply(this, arguments); 59 | 60 | // We will wait untill all these requests will be started. 61 | var dbPrefix = thaliConfig.BASE_DB_PATH + '/' + DB_NAME + '/'; 62 | var done = 0; 63 | return function (req) { 64 | if (req.path === '/NotificationBeacons') { 65 | done |= 1; 66 | } else if (req.path === dbPrefix + '_changes') { 67 | done |= 1 << 1; 68 | } else if (req.path === dbPrefix + '_bulk_docs') { 69 | done |= 1 << 2; 70 | } else if (req.path.indexOf(dbPrefix + '_local') === 0) { 71 | done |= 1 << 3; 72 | } 73 | if (done === 0xf) { 74 | done = -1; 75 | setImmediate(function () { 76 | resolve(); 77 | }); 78 | } 79 | return saltiFilter.apply(this, arguments); 80 | }; 81 | }); 82 | }); 83 | 84 | var ThaliManagerProxyquired = 85 | proxyquire('thali/NextGeneration/thaliManager', { 86 | 'salti': spySalti 87 | }); 88 | 89 | thaliManager = new ThaliManagerProxyquired( 90 | ExpressPouchDB, 91 | PouchDB, 92 | DB_NAME, 93 | ecdhForLocalDevice, 94 | new ThaliPeerPoolDefault() 95 | ); 96 | 97 | // This function will return all participant's public keys 98 | // except local 'publicKeyForLocalDevice' one. 99 | var partnerKeys = testUtils.turnParticipantsIntoBufferArray( 100 | t, publicKeyForLocalDevice 101 | ); 102 | 103 | // We are creating a local db for each participant. 104 | var pouchDB = new PouchDB(DB_NAME); 105 | 106 | pouchDB.put({ 107 | _id: publicBase64KeyForLocalDevice 108 | }) 109 | .then(function () { 110 | return thaliManager.start(partnerKeys); 111 | }) 112 | .then(function () { 113 | return allRequestsStarted; 114 | }) 115 | .then(function () { 116 | t.end(); 117 | }) 118 | }); 119 | -------------------------------------------------------------------------------- /test/www/jxcore/bv_tests/disabled/testThaliCryptoManager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var originalMobile = typeof Mobile === "undefined" ? undefined : Mobile; 4 | var mockMobile = require('./mockmobile'); 5 | var fs = require('fs-extra-promise'); 6 | var path = require('path'); 7 | var crypto = require('crypto'); 8 | var tape = require('../lib/thaliTape'); 9 | var cryptomanager = require('thali/thalicryptomanager'); 10 | var testUtils = require('../lib/testUtils.js'); 11 | 12 | // get the values needed for running the tests 13 | var configValues = cryptomanager.getConfigValuesForTestingOnly(); 14 | 15 | var fileLocation = path.join(testUtils.tmpDirectory(), './pkcs12folder'); 16 | 17 | // test setup & teardown activities 18 | var test = tape({ 19 | setup: function(t) { 20 | fs.ensureDirSync(fileLocation); 21 | global.Mobile = mockMobile; 22 | t.end(); 23 | }, 24 | teardown: function(t) { 25 | global.Mobile = originalMobile; 26 | fs.removeSync(fileLocation); 27 | t.end(); 28 | } 29 | }); 30 | 31 | test('successfully create a new pkcs12 file and return hash value', 32 | function(t) { 33 | var errorMessage = null; 34 | 35 | Mobile.setGetDocumentsPathReturnValues(errorMessage, fileLocation); 36 | 37 | cryptomanager.getPublicKeyHash(function (err, publicKeyHash) { 38 | t.equal(err, null); 39 | 40 | var file = path.join(fileLocation, configValues.pkcs12FileName); 41 | fs.readFile(file, function (err, pkcs12Content) { 42 | t.ifError(err); 43 | t.doesNotThrow(function() { 44 | var publicKey = crypto.pkcs12. 45 | extractPublicKey(configValues.password, pkcs12Content); 46 | t.ok(publicKey && publicKey.length > 0); 47 | t.equal(publicKeyHash, cryptomanager. 48 | generateSlicedSHA256Hash( 49 | publicKey, 50 | configValues.hashSizeInBytes)); 51 | t.end(); 52 | }); 53 | }); 54 | }); 55 | } 56 | ); 57 | 58 | test('successfully read a previous pkcs12 file and return hash value', 59 | function (t) { 60 | var errorMessage = null; 61 | 62 | Mobile.setGetDocumentsPathReturnValues(errorMessage, fileLocation); 63 | 64 | var pkcs12Content = crypto.pkcs12.createBundle(configValues.password, 65 | configValues.certname, configValues.country, configValues.organization); 66 | t.ok(pkcs12Content.length > 0); 67 | 68 | var file = path.join(fileLocation, configValues.pkcs12FileName); 69 | fs.writeFileSync(file, pkcs12Content, {flags: 'wx'}); 70 | t.doesNotThrow(function() { 71 | var publicKey = crypto.pkcs12. 72 | extractPublicKey(configValues.password, pkcs12Content); 73 | t.ok(publicKey && publicKey.length > 0); 74 | cryptomanager.getPublicKeyHash(function (err, publicKeyHash) { 75 | t.equal(err, null); 76 | t.equal(publicKeyHash, cryptomanager. 77 | generateSlicedSHA256Hash(publicKey, configValues.hashSizeInBytes)); 78 | t.end(); 79 | }); 80 | }); 81 | } 82 | ); 83 | 84 | test('failed to extract public key because of corrupt pkcs12 file', 85 | function (t) { 86 | var errorMessage = null, 87 | badFileLocation = path.join(__dirname, 'pkcs12folderbad'); 88 | 89 | var cryptoErrorMessage = 'error thrown by extractPublicKey() function'; 90 | 91 | Mobile.setGetDocumentsPathReturnValues(errorMessage, badFileLocation); 92 | 93 | cryptomanager.getPublicKeyHash(function (err, publicKeyHash) { 94 | t.equal(err.message, cryptoErrorMessage); 95 | t.end(); 96 | }); 97 | }); 98 | 99 | test('failed to get mobile documents path', function(t) { 100 | var errorMessage = 'GetDocumentsPath error', 101 | noFileLocation = null; 102 | Mobile.setGetDocumentsPathReturnValues(errorMessage, noFileLocation); 103 | cryptomanager.getPublicKeyHash(function (err, publicKeyHash) { 104 | t.equal(err, errorMessage); 105 | t.end(); 106 | }); 107 | }); 108 | --------------------------------------------------------------------------------