forked from cory/tildefriends
		
	
		
			
				
	
	
		
			1000 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1000 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "tests.h"
 | |
| 
 | |
| #include "bip39.h"
 | |
| #include "http.h"
 | |
| #include "log.h"
 | |
| #include "mem.h"
 | |
| #include "ssb.db.h"
 | |
| #include "ssb.h"
 | |
| #include "ssb.tests.h"
 | |
| #include "util.js.h"
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <inttypes.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include <uv.h>
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| #define WIFEXITED(x) 1
 | |
| #define WEXITSTATUS(x) (x)
 | |
| #endif
 | |
| 
 | |
| #if defined(__OpenBSD__)
 | |
| #include <sys/wait.h>
 | |
| #endif
 | |
| 
 | |
| #if defined(__APPLE__)
 | |
| #include <TargetConditionals.h>
 | |
| #endif
 | |
| 
 | |
| #define TEST_ARGS " --args=ssb_port=0,http_port=0"
 | |
| 
 | |
| #if !TARGET_OS_IPHONE
 | |
| static void _write_file(const char* path, const char* contents)
 | |
| {
 | |
| 	FILE* file = fopen(path, "w");
 | |
| 	if (!file)
 | |
| 	{
 | |
| 		tf_printf("Unable to write %s: %s.\n", path, strerror(errno));
 | |
| 		abort();
 | |
| 	}
 | |
| 	fputs(contents, file);
 | |
| 	fclose(file);
 | |
| }
 | |
| 
 | |
| static void _test_nop(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js", "print('hi');");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| }
 | |
| 
 | |
| static void _test_exception(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js", "throw new Error('oops');");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("result = %d\n", result);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) != 0);
 | |
| }
 | |
| 
 | |
| #if !defined(__HAIKU__)
 | |
| static void _test_sandbox(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.onExit = function(code, signal) {\n"
 | |
| 		"	print('child exited', code, signal);\n"
 | |
| 		"	if (code === 0 && signal === 0) {\n"
 | |
| 		"		exit(1);\n"
 | |
| 		"	}\n"
 | |
| 		"};\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(function() {\n"
 | |
| 		"		print('child started');\n"
 | |
| 		"	});\n"
 | |
| 		"});");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"print('Poking the sandbox.  This should fail.');\n"
 | |
| 		"let r = pokeSandbox();\n"
 | |
| 		"exit(r);\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void _test_child(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.onExit = function(code, signal) {\n"
 | |
| 		"	print('child exited', code, signal);\n"
 | |
| 		"	exit(code || signal);\n"
 | |
| 		"};\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(function() {\n"
 | |
| 		"		print('child started');\n"
 | |
| 		"	});\n"
 | |
| 		"});");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"print('I am the child process.');\n"
 | |
| 		"exit(0);\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| 
 | |
| static void _test_promise(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(function() {\n"
 | |
| 		"		task.getExports().then(function(exports) {\n"
 | |
| 		"			return exports.add(1, 1);\n"
 | |
| 		"		}).then(function(sum) {\n"
 | |
| 		"			if (sum == 2) {\n"
 | |
| 		"				exit(0);\n"
 | |
| 		"			} else {\n"
 | |
| 		"				exit(1);\n"
 | |
| 		"			}\n"
 | |
| 		"		});\n"
 | |
| 		"	});\n"
 | |
| 		"});\n");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"exports = {\n"
 | |
| 		"	add: function(left, right) {\n"
 | |
| 		"		return left + right;\n"
 | |
| 		"	}\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| 
 | |
| static void _test_promise_remote_throw(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(function() {\n"
 | |
| 		"		task.getExports().then(function(exp) {\n"
 | |
| 		"			return exp.add(1, 1);\n"
 | |
| 		"		}).then(function(sum) {\n"
 | |
| 		"			exit(1);\n"
 | |
| 		"		}).catch(function(error) {\n"
 | |
| 		"			print('Caught: ' + error.message);\n"
 | |
| 		"			if (error.stack) {\n"
 | |
| 		"				print('stack: ' + error.stack);\n"
 | |
| 		"			}\n"
 | |
| 		"			exit(0);\n"
 | |
| 		"		});\n"
 | |
| 		"	}).catch(function(e) {\n"
 | |
| 		"		print('caught', e.message);\n"
 | |
| 		"	});\n"
 | |
| 		"});\n");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"exports = {\n"
 | |
| 		"	add: function(left, right) {\n"
 | |
| 		"		throw new Error('fail');\n"
 | |
| 		"	}\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| 
 | |
| static void _test_promise_remote_reject(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(function() {\n"
 | |
| 		"		task.getExports().then(function(exp) {\n"
 | |
| 		"			return exp.add(1, 1);\n"
 | |
| 		"		}).then(function(sum) {\n"
 | |
| 		"			exit(1);\n"
 | |
| 		"		}).catch(function(error) {\n"
 | |
| 		"			print('Caught: ' + error.message);\n"
 | |
| 		"			if (error.stack) {\n"
 | |
| 		"				print('stack: ' + error.stack);\n"
 | |
| 		"			}\n"
 | |
| 		"			exit(0);\n"
 | |
| 		"		});\n"
 | |
| 		"	}).catch(function(e) {\n"
 | |
| 		"		print('caught', e.message);\n"
 | |
| 		"	});\n"
 | |
| 		"});\n");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"exports = {\n"
 | |
| 		"	add: function(left, right) {\n"
 | |
| 		"		return new Promise(function(resolve, reject) {\n"
 | |
| 		"			reject(new Error('oops'));\n"
 | |
| 		"		});\n"
 | |
| 		"	}\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| 
 | |
| static void _test_database(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"async function main() {\n"
 | |
| 		"	var db = new Database('testdb');\n"
 | |
| 		"	if (await db.get('a')) {\n"
 | |
| 		"		exit(1);\n"
 | |
| 		"	}\n"
 | |
| 		"	await db.set('a', 1);\n"
 | |
| 		"	if (await db.get('a') != 1) {\n"
 | |
| 		"		exit(2);\n"
 | |
| 		"	}\n"
 | |
| 		"	await db.exchange('b', null, 1);\n"
 | |
| 		"	await db.exchange('b', 1, 2);\n"
 | |
| 		"	if (await db.get('b') != 2) {\n"
 | |
| 		"		exit(5);\n"
 | |
| 		"	}\n"
 | |
| 		"	await db.set('c', 3);\n"
 | |
| 		"	await db.set('d', 3);\n"
 | |
| 		"	await db.remove('d', 3);\n"
 | |
| 		"	if (JSON.stringify(await db.getLike('b%')) != '{\"b\":\"2\"}') {\n"
 | |
| 		"		exit(6);\n"
 | |
| 		"	}\n"
 | |
| 		"\n"
 | |
| 		"	var expected = ['a', 'b', 'c'];\n"
 | |
| 		"	var have = await db.getAll();\n"
 | |
| 		"	for (var i = 0; i < have.length; i++) {\n"
 | |
| 		"		var item = have[i];\n"
 | |
| 		"		if (expected.indexOf(item) == -1) {\n"
 | |
| 		"			print('Did not find ' + item + ' in db.');\n"
 | |
| 		"			exit(3);\n"
 | |
| 		"		} else {\n"
 | |
| 		"			expected.splice(expected.indexOf(item), 1);\n"
 | |
| 		"		}\n"
 | |
| 		"	}\n"
 | |
| 		"	if (expected.length) {\n"
 | |
| 		"		print('Expected but did not find: ' + JSON.stringify(expected));\n"
 | |
| 		"		exit(4);\n"
 | |
| 		"	}\n"
 | |
| 		"	if (JSON.stringify(await databases.list('%')) != '[\"core\",\"testdb\"]') {\n"
 | |
| 		"		exit(7);\n"
 | |
| 		"	}\n"
 | |
| 		"}\n"
 | |
| 		"main();");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| }
 | |
| 
 | |
| static void _test_this(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.activate.bind(null).apply();\n"
 | |
| 		"exit(0);\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| }
 | |
| 
 | |
| static void _test_await(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"print('hi');\n"
 | |
| 		"function foobar() {\n"
 | |
| 		"	return new Promise(function(resolve, reject) {\n"
 | |
| 		"		resolve(10);\n"
 | |
| 		"	});\n"
 | |
| 		"}\n"
 | |
| 		"\n"
 | |
| 		"async function huh() {\n"
 | |
| 		"	let v = await foobar();\n"
 | |
| 		"	print('v => ' + v);\n"
 | |
| 		"	if (v != 10) {\n"
 | |
| 		"		throw new Error('nope');\n"
 | |
| 		"	}\n"
 | |
| 		"}\n"
 | |
| 		"\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| }
 | |
| 
 | |
| static void _test_import(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"import * as req from './required.js';\n"
 | |
| 		"if (req.foo() != 12345) {\n"
 | |
| 		"	exit(1);\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	_write_file("out/required.js",
 | |
| 		"export function foo() {\n"
 | |
| 		"	return 12345;\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	_write_file("out/bad.js",
 | |
| 		"import * as req from './missing.js';\n"
 | |
| 		"if (req.foo() != 12345) {\n"
 | |
| 		"	exit(1);\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/bad.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) != 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/required.js");
 | |
| 	unlink("out/missing.js");
 | |
| }
 | |
| 
 | |
| static void _test_exit(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js", "import * as blah from './blah.js';\n");
 | |
| 	_write_file("out/blah.js", "\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/blah.js");
 | |
| }
 | |
| 
 | |
| static void _test_icu(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"print('Hi');\n"
 | |
| 		"print(parseInt('3').toLocaleString());\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| }
 | |
| 
 | |
| static void _test_uint8array(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.onExit = function() {\n"
 | |
| 		"	print('child exited');\n"
 | |
| 		"};\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(async function() {\n"
 | |
| 		"		print('child started');\n"
 | |
| 		"		var input = new Uint8Array(10);\n"
 | |
| 		"		for (var i = 0; i < 10; i++) {\n"
 | |
| 		"			input[i] = i;\n"
 | |
| 		"		}\n"
 | |
| 		"		var test = (await task.getExports()).test;\n"
 | |
| 		"		var output = new Uint8Array(await test(input));\n"
 | |
| 		"		print('input', input, input.length, input.byteLength);\n"
 | |
| 		"		print('output', output, output.length, output.byteLength);\n"
 | |
| 		"		for (var i = 0; i < 10; i++) {\n"
 | |
| 		"			print(output[i]);\n"
 | |
| 		"			if (output[i] != i) {\n"
 | |
| 		"				print('output[' + i + '] == ' + output[i]);\n"
 | |
| 		"				exit(1);\n"
 | |
| 		"			}\n"
 | |
| 		"		}\n"
 | |
| 		"		exit(0);\n"
 | |
| 		"	})\n"
 | |
| 		"})\n");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"exports = {\n"
 | |
| 		"	test: function(data) {\n"
 | |
| 		"		return data;\n"
 | |
| 		"	}\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| 
 | |
| static void _test_float(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"var task = new Task();\n"
 | |
| 		"task.onExit = function() {\n"
 | |
| 		"	print('child exited');\n"
 | |
| 		"};\n"
 | |
| 		"task.activate();\n"
 | |
| 		"File.readFile('out/child.js').then(function(data) {\n"
 | |
| 		"	task.execute({name: 'child.js', source: utf8Decode(data)}).then(async function() {\n"
 | |
| 		"		print('get exports');\n"
 | |
| 		"		let test = (await task.getExports()).test;\n"
 | |
| 		"		print('calling export');\n"
 | |
| 		"		let result = await test(1.2);\n"
 | |
| 		"		print(result);\n"
 | |
| 		"		exit(result == 1.2 ? 0 : 1);\n"
 | |
| 		"	});\n"
 | |
| 		"});");
 | |
| 
 | |
| 	_write_file("out/child.js",
 | |
| 		"print(\"child\");\n"
 | |
| 		"exports = {\n"
 | |
| 		"	test: function(value) {\n"
 | |
| 		"		print(value);\n"
 | |
| 		"		return value;\n"
 | |
| 		"	}\n"
 | |
| 		"};\n"
 | |
| 		"print(\"child ready\");\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	(void)result;
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| 	unlink("out/child.js");
 | |
| }
 | |
| 
 | |
| static void _test_file(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"'use strict';\n"
 | |
| 		"File.readFile('out/test.js').then(function(data) {\n"
 | |
| 		"}).catch(function(error) {\n"
 | |
| 		"	print('ERROR', error);\n"
 | |
| 		"	exit(1);\n"
 | |
| 		"});\n"
 | |
| 		"File.readFile('out/missing.txt').then(function(data) {\n"
 | |
| 		"	print('READ', utf8Decode(data));\n"
 | |
| 		"	exit(1);\n"
 | |
| 		"}).catch(function(error) {\n"
 | |
| 		"	print('expected error', error);\n"
 | |
| 		"});\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test.db -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| }
 | |
| 
 | |
| static void _test_b64(const tf_test_options_t* options)
 | |
| {
 | |
| 	_write_file("out/test.js",
 | |
| 		"'use strict';\n"
 | |
| 		"print(base64Encode('hello'));\n"
 | |
| 		"let x = utf8Decode(base64Decode(base64Encode('hello')));\n"
 | |
| 		"if (x !== 'hello') {\n"
 | |
| 		"	print(x);\n"
 | |
| 		"	exit(1);\n"
 | |
| 		"}\n");
 | |
| 
 | |
| 	char command[256];
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
 | |
| 	tf_printf("%s\n", command);
 | |
| 	int result = system(command);
 | |
| 	tf_printf("returned %d\n", WEXITSTATUS(result));
 | |
| 	assert(WIFEXITED(result));
 | |
| 	assert(WEXITSTATUS(result) == 0);
 | |
| 
 | |
| 	unlink("out/test.js");
 | |
| }
 | |
| 
 | |
| static const char* k_bip39_test_key_base64 = "GO0Lv5BvcuuJJdHrokHoo0PmCDC/XjO/SZ6H+ddq4UvWd/VPW1RJrjd1aCUIfPIojFXrWMb8R54vVerU2TwjdQ==.ed25519";
 | |
| static const char* k_bip32_test_key_words =
 | |
| 	"body hair useful camp warm into cause riot two bamboo kick educate dinosaur advice seed type crisp where guilt avocado output rely lunch goddess";
 | |
| 
 | |
| static void _test_bip39(const tf_test_options_t* options)
 | |
| {
 | |
| 	uint8_t bytes[64] = { 0 };
 | |
| 	tf_base64_decode(k_bip39_test_key_base64, strlen(k_bip39_test_key_base64) - strlen(".ed25519"), bytes, sizeof(bytes));
 | |
| 	char words[4096] = "";
 | |
| 	bool result = tf_bip39_bytes_to_words(bytes, sizeof(bytes) / 2, words, sizeof(words));
 | |
| 	tf_printf("%d: %s\n", result, words);
 | |
| 	assert(result);
 | |
| 	assert(strcmp(words, k_bip32_test_key_words) == 0);
 | |
| 
 | |
| 	uint8_t test_bytes[32] = { 0 };
 | |
| 	result = tf_bip39_words_to_bytes(k_bip32_test_key_words, test_bytes, sizeof(test_bytes));
 | |
| 	assert(result);
 | |
| 	assert(memcmp(bytes, test_bytes, sizeof(test_bytes)) == 0);
 | |
| }
 | |
| 
 | |
| typedef struct _test_http_t
 | |
| {
 | |
| 	uv_loop_t* loop;
 | |
| 	uv_async_t async;
 | |
| 	bool done;
 | |
| } test_http_t;
 | |
| 
 | |
| static void _test_http_async(uv_async_t* async)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void _test_http_thread(void* data)
 | |
| {
 | |
| 	test_http_t* test = data;
 | |
| 	const char* value = tf_http_get_cookie("a=foo; b=bar", "a");
 | |
| 	assert(strcmp(value, "foo") == 0);
 | |
| 	tf_free((void*)value);
 | |
| 	value = tf_http_get_cookie("a=foo; b=bar", "b");
 | |
| 	assert(strcmp(value, "bar") == 0);
 | |
| 	tf_free((void*)value);
 | |
| 	assert(tf_http_get_cookie("a=foo; b=bar", "c") == NULL);
 | |
| 
 | |
| 	int r = system("curl -v http://localhost:23456/404");
 | |
| 	assert(WEXITSTATUS(r) == 0);
 | |
| 	tf_printf("curl returned %d\n", WEXITSTATUS(r));
 | |
| 
 | |
| 	r = system("curl -v http://localhost:23456/hello");
 | |
| 	assert(WEXITSTATUS(r) == 0);
 | |
| 	tf_printf("curl returned %d\n", WEXITSTATUS(r));
 | |
| 
 | |
| 	r = system("curl -v --data 'hello world' http://localhost:23456/post");
 | |
| 	assert(WEXITSTATUS(r) == 0);
 | |
| 	tf_printf("curl returned %d\n", WEXITSTATUS(r));
 | |
| 
 | |
| 	r = system("curl -v http://localhost:23456/hello http://localhost:23456/hello http://localhost:23456/hello");
 | |
| 	assert(WEXITSTATUS(r) == 0);
 | |
| 	tf_printf("curl returned %d\n", WEXITSTATUS(r));
 | |
| 
 | |
| 	test->done = true;
 | |
| 
 | |
| 	/* All to wake up the loop. */
 | |
| 	uv_async_send(&test->async);
 | |
| }
 | |
| 
 | |
| static void _test_http_handler(tf_http_request_t* request)
 | |
| {
 | |
| 	const char* headers[] = {
 | |
| 		"User-Agent",
 | |
| 		"TildeFriends/1.0",
 | |
| 	};
 | |
| 	const char* k_payload = "Hello, world!\n";
 | |
| 	tf_http_respond(request, 200, headers, 1, k_payload, strlen(k_payload));
 | |
| }
 | |
| 
 | |
| static void _test_http_handler_post(tf_http_request_t* request)
 | |
| {
 | |
| 	const void* body = NULL;
 | |
| 	size_t size = tf_http_get_body(request, &body);
 | |
| 	tf_printf("size = %zd body=%.*s\n", size, (int)size, (const char*)body);
 | |
| 	const char* headers[] = {
 | |
| 		"Connection",
 | |
| 		"close",
 | |
| 	};
 | |
| 	const char* k_payload = "Hello, world!\n";
 | |
| 	tf_http_respond(request, 200, headers, 1, k_payload, strlen(k_payload));
 | |
| }
 | |
| 
 | |
| static void _test_http(const tf_test_options_t* options)
 | |
| {
 | |
| 	tf_printf("Starting http.\n");
 | |
| 	uv_loop_t loop = { 0 };
 | |
| 	uv_loop_init(&loop);
 | |
| 	tf_http_t* http = tf_http_create(&loop);
 | |
| 	tf_http_add_handler(http, "/hello", _test_http_handler, NULL, NULL);
 | |
| 	tf_http_add_handler(http, "/post", _test_http_handler_post, NULL, NULL);
 | |
| 	tf_http_listen(http, 23456, true, NULL, NULL);
 | |
| 
 | |
| 	test_http_t test = { .loop = &loop };
 | |
| 	uv_async_init(&loop, &test.async, _test_http_async);
 | |
| 	uv_thread_t thread = { 0 };
 | |
| 	uv_thread_create(&thread, _test_http_thread, &test);
 | |
| 	while (!test.done)
 | |
| 	{
 | |
| 		uv_run(&loop, UV_RUN_ONCE);
 | |
| 	}
 | |
| 	uv_close((uv_handle_t*)&test.async, NULL);
 | |
| 	tf_printf("Done running.\n");
 | |
| 
 | |
| 	tf_http_destroy(http);
 | |
| 	uv_run(&loop, UV_RUN_DEFAULT);
 | |
| 	uv_loop_close(&loop);
 | |
| 
 | |
| 	uv_thread_join(&thread);
 | |
| }
 | |
| 
 | |
| static int _http_get_status_code(const char* url)
 | |
| {
 | |
| 	char command[1024];
 | |
| 	snprintf(command, sizeof(command), "curl -s -o /dev/null -w '%%{http_code}' \"%s\"", url);
 | |
| 	char buffer[256] = "";
 | |
| 	FILE* file = popen(command, "r");
 | |
| 	char* result = fgets(buffer, sizeof(buffer), file);
 | |
| 	pclose(file);
 | |
| 	return result ? atoi(result) : -1;
 | |
| }
 | |
| 
 | |
| static void _http_check_status_code(const char* url, int expected_code)
 | |
| {
 | |
| 	char command[1024];
 | |
| 	snprintf(command, sizeof(command), "curl -s -o /dev/null -w '%%{http_code}' \"%s\"", url);
 | |
| 	char buffer[256] = "";
 | |
| 	FILE* file = popen(command, "r");
 | |
| 	char* result = fgets(buffer, sizeof(buffer), file);
 | |
| 	tf_printf("%s => %s\n", command, result);
 | |
| 	assert(atoi(buffer) == expected_code);
 | |
| 	assert(file);
 | |
| 	int status = pclose(file);
 | |
| 	(void)status;
 | |
| 	assert(WEXITSTATUS(status) == 0);
 | |
| }
 | |
| 
 | |
| static void _http_check_body_contains(const char* url, const char* expected)
 | |
| {
 | |
| 	char command[1024];
 | |
| 	snprintf(command, sizeof(command), "curl -s \"%s\"", url);
 | |
| 	char buffer[1024] = "";
 | |
| 	FILE* file = popen(command, "r");
 | |
| 	bool found = false;
 | |
| 	while (!found)
 | |
| 	{
 | |
| 		char* result = fgets(buffer, sizeof(buffer), file);
 | |
| 		if (!result)
 | |
| 		{
 | |
| 			break;
 | |
| 		}
 | |
| 		found = strstr(buffer, expected) != NULL;
 | |
| 		if (found)
 | |
| 		{
 | |
| 			tf_printf("%s => found: \"%s\"\n", url, expected);
 | |
| 		}
 | |
| 	}
 | |
| 	if (!found)
 | |
| 	{
 | |
| 		tf_printf("Didn't find \"%s\" in %s.\n", expected, url);
 | |
| 	}
 | |
| 	assert(found);
 | |
| 	assert(file);
 | |
| 	int status = pclose(file);
 | |
| 	(void)status;
 | |
| 	assert(WEXITSTATUS(status) == 0);
 | |
| }
 | |
| 
 | |
| static void _test_httpd(const tf_test_options_t* options)
 | |
| {
 | |
| 	uv_loop_t loop = { 0 };
 | |
| 	uv_loop_init(&loop);
 | |
| 
 | |
| 	unlink("out/test_db0.sqlite");
 | |
| 	uv_stdio_container_t stdio[] = {
 | |
| 		[STDIN_FILENO] = { .flags = UV_IGNORE },
 | |
| 		[STDOUT_FILENO] = { .flags = UV_INHERIT_FD },
 | |
| 		[STDERR_FILENO] = { .flags = UV_INHERIT_FD },
 | |
| 	};
 | |
| 	uv_process_t process = { 0 };
 | |
| 	uv_spawn(&loop, &process,
 | |
| 		&(uv_process_options_t) {
 | |
| 			.file = options->exe_path,
 | |
| 			.args = (char*[]) { (char*)options->exe_path, "run", "--db-path=out/test_db0.sqlite", "--args=ssb_port=0,http_port=8080", NULL },
 | |
| 			.stdio_count = sizeof(stdio) / sizeof(*stdio),
 | |
| 			.stdio = stdio,
 | |
| 		});
 | |
| 
 | |
| 	for (int i = 0; i < 100; i++)
 | |
| 	{
 | |
| 		if (_http_get_status_code("http://localhost:8080/debug") == 200)
 | |
| 		{
 | |
| 			break;
 | |
| 		}
 | |
| 		uv_sleep(1000);
 | |
| 	}
 | |
| 
 | |
| #if !defined(__HAIKU__)
 | |
| 	tf_ssb_t* ssb = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
 | |
| 	const char* app_id = tf_ssb_db_get_property(ssb, "core", "path:test");
 | |
| 	tf_ssb_destroy(ssb);
 | |
| #endif
 | |
| 
 | |
| 	_http_check_status_code("http://localhost:8080/404", 404);
 | |
| 	_http_check_status_code("http://localhost:8080/", 303);
 | |
| 	_http_check_status_code("http://localhost:8080/~core/apps/", 200);
 | |
| 	_http_check_status_code("http://localhost:8080/~core/apps", 303);
 | |
| 	_http_check_status_code("http://localhost:8080/~core/apps/view", 200);
 | |
| 	_http_check_body_contains("http://localhost:8080/~core/apps/", "<title>Tilde Friends</title>");
 | |
| 	_http_check_body_contains("http://localhost:8080/~core/apps/view", "\"type\":\"tildefriends-app\"");
 | |
| 	_http_check_body_contains("http://localhost:8080/~core/test/hello.txt", "Hello, world!");
 | |
| 	_http_check_status_code("http://localhost:8080/~core/test/nonexistent.txt", 404);
 | |
| 	_http_check_body_contains("http://localhost:8080/&MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=.sha256/view", "Hello, world!");
 | |
| 
 | |
| #if !defined(__HAIKU__)
 | |
| 	char url[1024];
 | |
| 	snprintf(url, sizeof(url), "http://localhost:8080/%s/", app_id);
 | |
| 	_http_check_body_contains(url, "<title>Tilde Friends</title>");
 | |
| 	snprintf(url, sizeof(url), "http://localhost:8080/%s/view", app_id);
 | |
| 	_http_check_body_contains(url, "\"type\":\"tildefriends-app\"");
 | |
| 	snprintf(url, sizeof(url), "http://localhost:8080/%s/hello.txt", app_id);
 | |
| 	_http_check_body_contains(url, "Hello, world!");
 | |
| 	tf_free((void*)app_id);
 | |
| #endif
 | |
| 
 | |
| 	uv_process_kill(&process, SIGTERM);
 | |
| 	uv_close((uv_handle_t*)&process, NULL);
 | |
| 	uv_run(&loop, UV_RUN_ONCE);
 | |
| 
 | |
| 	uv_loop_close(&loop);
 | |
| }
 | |
| 
 | |
| static void _test_pattern(const tf_test_options_t* options)
 | |
| {
 | |
| 	assert(tf_http_pattern_matches("/~core/test/", "/~core/test/"));
 | |
| 	assert(tf_http_pattern_matches("/~core/test/*", "/~core/test/"));
 | |
| 	assert(tf_http_pattern_matches("/~core/test/*", "/~core/test/blah"));
 | |
| 	assert(tf_http_pattern_matches("*/~core/test/", "/~core/test/"));
 | |
| 	assert(tf_http_pattern_matches("*/~core/test/", "blah/~core/test/"));
 | |
| 	assert(tf_http_pattern_matches("/~*/*/", "/~core/test/"));
 | |
| 	assert(tf_http_pattern_matches("/~{word}/*", "/~core/test"));
 | |
| 	assert(tf_http_pattern_matches("/~{word}/{word}/", "/~core/test/"));
 | |
| 	assert(tf_http_pattern_matches("/~{word}/{word}", "/~core/test"));
 | |
| 	assert(!tf_http_pattern_matches("/~{word}/{word}", "/~foo/bar/baz"));
 | |
| }
 | |
| 
 | |
| static void _test_auto_process_exit(uv_process_t* process, int64_t status, int termination_signal)
 | |
| {
 | |
| 	tf_printf("Process exit %" PRId64 " signal=%d.\n", status, termination_signal);
 | |
| 	assert(status == 0);
 | |
| 	process->data = NULL;
 | |
| 	uv_close((uv_handle_t*)process, NULL);
 | |
| }
 | |
| 
 | |
| static void _test_auto(const tf_test_options_t* options)
 | |
| {
 | |
| 	uv_loop_t loop = { 0 };
 | |
| 	uv_loop_init(&loop);
 | |
| 
 | |
| 	char executable[1024];
 | |
| 	size_t size = sizeof(executable);
 | |
| 	uv_exepath(executable, &size);
 | |
| 
 | |
| 	unlink("out/selenium.sqlite");
 | |
| 	unlink("out/selenium.sqlite-shm");
 | |
| 	unlink("out/selenium.sqlite-wal");
 | |
| 
 | |
| 	char* args[] = { executable, "run", "-d", "out/selenium.sqlite", "-a", "ssb_port=0,http_port=8888", NULL };
 | |
| 
 | |
| 	uv_stdio_container_t io[3] = {
 | |
| 		{ .flags = UV_INHERIT_FD },
 | |
| 		{ .flags = UV_INHERIT_FD },
 | |
| 		{ .flags = UV_INHERIT_FD },
 | |
| 	};
 | |
| 
 | |
| 	uv_process_options_t process_options = { 0 };
 | |
| 	process_options.args = args;
 | |
| 	process_options.exit_cb = _test_auto_process_exit;
 | |
| 	process_options.stdio = io;
 | |
| 	process_options.stdio_count = sizeof(io) / sizeof(*io);
 | |
| 	process_options.file = args[0];
 | |
| 
 | |
| 	uv_process_t process = { .data = &process };
 | |
| 	int spawn_result = uv_spawn(&loop, &process, &process_options);
 | |
| 	if (spawn_result)
 | |
| 	{
 | |
| 		tf_printf("uv_spawn: %s\n", uv_strerror(spawn_result));
 | |
| 		abort();
 | |
| 	}
 | |
| 
 | |
| 	char* selenium_args[] = { "python3", "tools/autotest.py", NULL };
 | |
| 	process_options.args = selenium_args;
 | |
| 	process_options.exit_cb = _test_auto_process_exit;
 | |
| 	process_options.file = selenium_args[0];
 | |
| 
 | |
| 	uv_process_t selenium = { .data = &selenium };
 | |
| 	spawn_result = uv_spawn(&loop, &selenium, &process_options);
 | |
| 	if (spawn_result)
 | |
| 	{
 | |
| 		tf_printf("uv_spawn: %s\n", uv_strerror(spawn_result));
 | |
| 		abort();
 | |
| 	}
 | |
| 
 | |
| 	while (selenium.data)
 | |
| 	{
 | |
| 		uv_run(&loop, UV_RUN_ONCE);
 | |
| 	}
 | |
| 	uv_process_kill(&process, SIGTERM);
 | |
| 	uv_run(&loop, UV_RUN_DEFAULT);
 | |
| 	uv_loop_close(&loop);
 | |
| }
 | |
| 
 | |
| static void _tf_test_run(const tf_test_options_t* options, const char* name, void (*test)(const tf_test_options_t* options), bool opt_in)
 | |
| {
 | |
| 	bool specified = false;
 | |
| 	if (options->tests)
 | |
| 	{
 | |
| 		char* dup = tf_strdup(options->tests);
 | |
| 		char* state = NULL;
 | |
| 		const char* t = NULL;
 | |
| 		while ((t = strtok_r(t ? NULL : dup, ",", &state)) != NULL)
 | |
| 		{
 | |
| 			if (strcmp(t, name) == 0)
 | |
| 			{
 | |
| 				specified = true;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		tf_free(dup);
 | |
| 	}
 | |
| 
 | |
| 	if ((!opt_in && !options->tests) || specified)
 | |
| 	{
 | |
| #define GREEN "\e[1;32m"
 | |
| #define MAGENTA "\e[1;35m"
 | |
| #define CYAN "\e[1;36m"
 | |
| #define RESET "\e[0m"
 | |
| 		tf_printf(CYAN "== running test " MAGENTA "%s" CYAN " ==" RESET "\n", name);
 | |
| 		test(options);
 | |
| 		tf_printf("[" GREEN "pass" RESET "] %s\n", name);
 | |
| #undef GREEN
 | |
| #undef MAGENTA
 | |
| #undef CYAN
 | |
| #undef RESET
 | |
| 	}
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void tf_tests(const tf_test_options_t* options)
 | |
| {
 | |
| #if !TARGET_OS_IPHONE
 | |
| 	_tf_test_run(options, "bip39", _test_bip39, false);
 | |
| 	_tf_test_run(options, "http", _test_http, false);
 | |
| 	_tf_test_run(options, "httpd", _test_httpd, false);
 | |
| 	_tf_test_run(options, "pattern", _test_pattern, false);
 | |
| 	_tf_test_run(options, "ssb", tf_ssb_test_ssb, false);
 | |
| 	_tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion, false);
 | |
| 	_tf_test_run(options, "ssb_following", tf_ssb_test_following, false);
 | |
| 	_tf_test_run(options, "nop", _test_nop, false);
 | |
| 	_tf_test_run(options, "exception", _test_exception, false);
 | |
| #if !defined(__HAIKU__)
 | |
| 	_tf_test_run(options, "sandbox", _test_sandbox, false);
 | |
| #endif
 | |
| 	_tf_test_run(options, "child", _test_child, false);
 | |
| 	_tf_test_run(options, "promise", _test_promise, false);
 | |
| 	_tf_test_run(options, "promise_remote_throw", _test_promise_remote_throw, false);
 | |
| 	_tf_test_run(options, "promise_remote_reject", _test_promise_remote_reject, false);
 | |
| 	_tf_test_run(options, "database", _test_database, false);
 | |
| 	_tf_test_run(options, "this", _test_this, false);
 | |
| 	_tf_test_run(options, "await", _test_await, false);
 | |
| 	_tf_test_run(options, "import", _test_import, false);
 | |
| 	_tf_test_run(options, "exit", _test_exit, false);
 | |
| 	_tf_test_run(options, "icu", _test_icu, false);
 | |
| 	_tf_test_run(options, "uint8array", _test_uint8array, false);
 | |
| 	_tf_test_run(options, "float", _test_float, false);
 | |
| 	_tf_test_run(options, "file", _test_file, false);
 | |
| 	_tf_test_run(options, "b64", _test_b64, false);
 | |
| 	_tf_test_run(options, "rooms", tf_ssb_test_rooms, false);
 | |
| 	_tf_test_run(options, "bench", tf_ssb_test_bench, false);
 | |
| 	_tf_test_run(options, "auto", _test_auto, false);
 | |
| 	_tf_test_run(options, "go-ssb-room", tf_ssb_test_go_ssb_room, true);
 | |
| 	_tf_test_run(options, "encrypt", tf_ssb_test_encrypt, false);
 | |
| 	_tf_test_run(options, "peer_exchange", tf_ssb_test_peer_exchange, false);
 | |
| 	_tf_test_run(options, "publish", tf_ssb_test_publish, false);
 | |
| 	_tf_test_run(options, "replicate", tf_ssb_test_replicate, false);
 | |
| 	_tf_test_run(options, "replicate_blob", tf_ssb_test_replicate_blob, false);
 | |
| 	_tf_test_run(options, "connect_str", tf_ssb_test_connect_str, false);
 | |
| 	_tf_test_run(options, "invite", tf_ssb_test_invite, false);
 | |
| 	_tf_test_run(options, "triggers", tf_ssb_test_triggers, false);
 | |
| 	_tf_test_run(options, "cli", tf_ssb_test_cli, false);
 | |
| 	_tf_test_run(options, "following_perf", tf_ssb_test_following_perf, true);
 | |
| 	tf_printf("Tests completed.\n");
 | |
| #endif
 | |
| }
 |