提交 80cb6863 编写于 作者: T Tony Gentilcore

Merge pull request #310 from tonygentilcore/roll

Update to mojo 402986f19a165b3be13108146a913245c2c36d7c
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This variable should point to the Dart SDK.
dart_sdk_root = "//third_party/dart-sdk/dart-sdk"
......@@ -10151,7 +10151,7 @@ extern const NameToFunc g_gles2_function_table[] = {
code = """
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h"
#include "mojo/public/c/gles2/gles2.h"
namespace mojo {
......@@ -10186,9 +10186,9 @@ class MojoGLES2Impl : public gpu::gles2::GLES2Interface {
#include "mojo/gpu/mojo_gles2_impl_autogen.h"
#include "base/logging.h"
#include "third_party/mojo/src/mojo/public/c/gles2/chromium_sync_point.h"
#include "third_party/mojo/src/mojo/public/c/gles2/chromium_texture_mailbox.h"
#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h"
#include "mojo/public/c/gles2/chromium_sync_point.h"
#include "mojo/public/c/gles2/chromium_texture_mailbox.h"
#include "mojo/public/c/gles2/gles2.h"
namespace mojo {
......@@ -10504,128 +10504,6 @@ const size_t GLES2Util::enum_to_string_table_len_ =
file.Close()
def WritePepperGLES2Implementation(self, filename):
"""Writes the Pepper OpenGLES interface implementation."""
file = CWriter(filename)
file.Write(_LICENSE)
file.Write(_DO_NOT_EDIT_WARNING)
file.Write("#include \"ppapi/shared_impl/ppb_opengles2_shared.h\"\n\n")
file.Write("#include \"base/logging.h\"\n")
file.Write("#include \"gpu/command_buffer/client/gles2_implementation.h\"\n")
file.Write("#include \"ppapi/shared_impl/ppb_graphics_3d_shared.h\"\n")
file.Write("#include \"ppapi/thunk/enter.h\"\n\n")
file.Write("namespace ppapi {\n\n")
file.Write("namespace {\n\n")
file.Write("typedef thunk::EnterResource<thunk::PPB_Graphics3D_API>"
" Enter3D;\n\n")
file.Write("gpu::gles2::GLES2Implementation* ToGles2Impl(Enter3D*"
" enter) {\n")
file.Write(" DCHECK(enter);\n")
file.Write(" DCHECK(enter->succeeded());\n")
file.Write(" return static_cast<PPB_Graphics3D_Shared*>(enter->object())->"
"gles2_impl();\n");
file.Write("}\n\n");
for func in self.original_functions:
if not func.InAnyPepperExtension():
continue
original_arg = func.MakeTypedPepperArgString("")
context_arg = "PP_Resource context_id"
if len(original_arg):
arg = context_arg + ", " + original_arg
else:
arg = context_arg
file.Write("%s %s(%s) {\n" %
(func.return_type, func.GetPepperName(), arg))
file.Write(" Enter3D enter(context_id, true);\n")
file.Write(" if (enter.succeeded()) {\n")
return_str = "" if func.return_type == "void" else "return "
file.Write(" %sToGles2Impl(&enter)->%s(%s);\n" %
(return_str, func.original_name,
func.MakeOriginalArgString("")))
file.Write(" }")
if func.return_type == "void":
file.Write("\n")
else:
file.Write(" else {\n")
file.Write(" return %s;\n" % func.GetErrorReturnString())
file.Write(" }\n")
file.Write("}\n\n")
file.Write("} // namespace\n")
for interface in self.pepper_interfaces:
file.Write("const %s* PPB_OpenGLES2_Shared::Get%sInterface() {\n" %
(interface.GetStructName(), interface.GetName()))
file.Write(" static const struct %s "
"ppb_opengles2 = {\n" % interface.GetStructName())
file.Write(" &")
file.Write(",\n &".join(
f.GetPepperName() for f in self.original_functions
if f.InPepperInterface(interface)))
file.Write("\n")
file.Write(" };\n")
file.Write(" return &ppb_opengles2;\n")
file.Write("}\n")
file.Write("} // namespace ppapi\n")
file.Close()
self.generated_cpp_filenames.append(file.filename)
def WriteGLES2ToPPAPIBridge(self, filename):
"""Connects GLES2 helper library to PPB_OpenGLES2 interface"""
file = CWriter(filename)
file.Write(_LICENSE)
file.Write(_DO_NOT_EDIT_WARNING)
file.Write("#ifndef GL_GLEXT_PROTOTYPES\n")
file.Write("#define GL_GLEXT_PROTOTYPES\n")
file.Write("#endif\n")
file.Write("#include <GLES2/gl2.h>\n")
file.Write("#include <GLES2/gl2ext.h>\n")
file.Write("#include \"ppapi/lib/gl/gles2/gl2ext_ppapi.h\"\n\n")
for func in self.original_functions:
if not func.InAnyPepperExtension():
continue
interface = self.interface_info[func.GetInfo('pepper_interface') or '']
file.Write("%s GL_APIENTRY gl%s(%s) {\n" %
(func.return_type, func.GetPepperName(),
func.MakeTypedPepperArgString("")))
return_str = "" if func.return_type == "void" else "return "
interface_str = "glGet%sInterfacePPAPI()" % interface.GetName()
original_arg = func.MakeOriginalArgString("")
context_arg = "glGetCurrentContextPPAPI()"
if len(original_arg):
arg = context_arg + ", " + original_arg
else:
arg = context_arg
if interface.GetName():
file.Write(" const struct %s* ext = %s;\n" %
(interface.GetStructName(), interface_str))
file.Write(" if (ext)\n")
file.Write(" %sext->%s(%s);\n" %
(return_str, func.GetPepperName(), arg))
if return_str:
file.Write(" %s0;\n" % return_str)
else:
file.Write(" %s%s->%s(%s);\n" %
(return_str, interface_str, func.GetPepperName(), arg))
file.Write("}\n\n")
file.Close()
self.generated_cpp_filenames.append(file.filename)
def WriteMojoGLCallVisitor(self, filename):
"""Provides the GL implementation for mojo"""
file = CWriter(filename)
......@@ -10670,10 +10548,6 @@ def Format(generated_files):
def main(argv):
"""This is the main function."""
parser = OptionParser()
parser.add_option(
"--output-dir",
help="base directory for resulting files, under chrome/src. default is "
"empty. Use this if you want the result stored under gen.")
parser.add_option(
"-v", "--verbose", action="store_true",
help="prints more output.")
......@@ -10702,20 +10576,9 @@ def main(argv):
# This script lives under gpu/command_buffer, cd to base directory.
os.chdir(os.path.dirname(__file__) + "/../..")
base_dir = os.getcwd()
gen = GLGenerator(options.verbose)
gen.ParseGLH("gpu/command_buffer/cmd_buffer_functions.txt")
# Support generating files under gen/
if options.output_dir != None:
os.chdir(options.output_dir)
gen.WritePepperGLES2Interface("ppapi/api/ppb_opengles2.idl", False)
gen.WritePepperGLES2Interface("ppapi/api/dev/ppb_opengles2ext_dev.idl", True)
gen.WriteGLES2ToPPAPIBridge("ppapi/lib/gl/gles2/gles2.c")
gen.WritePepperGLES2Implementation(
"ppapi/shared_impl/ppb_opengles2_shared.cc")
os.chdir(base_dir)
gen.WriteCommandIds("gpu/command_buffer/common/gles2_cmd_ids_autogen.h")
gen.WriteFormat("gpu/command_buffer/common/gles2_cmd_format_autogen.h")
gen.WriteFormatTest(
......@@ -10769,8 +10632,7 @@ def main(argv):
gen.WriteCommonUtilsImpl(
"gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h")
gen.WriteGLES2Header("gpu/GLES2/gl2chromium_autogen.h")
mojo_gles2_prefix = ("third_party/mojo/src/mojo/public/c/gles2/"
"gles2_call_visitor")
mojo_gles2_prefix = ("mojo/public/c/gles2/gles2_call_visitor")
gen.WriteMojoGLCallVisitor(mojo_gles2_prefix + "_autogen.h")
gen.WriteMojoGLCallVisitorForExtension(
mojo_gles2_prefix + "_chromium_texture_mailbox_autogen.h",
......
......@@ -14,6 +14,7 @@ import re
# NOTE: The EDK allows all external paths, so doesn't need a whitelist.
_PACKAGE_WHITELISTED_EXTERNAL_PATHS = {
"SDK": ["//build/module_args/mojo.gni",
"//build/module_args/dart.gni",
"//testing/gtest",
"//third_party/cython",
"//third_party/khronos"],
......
......@@ -9,6 +9,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
import org.chromium.mojo.bindings.test.mojom.imported.ImportedInterface;
import org.chromium.mojo.bindings.test.mojom.ping.PingService;
import org.chromium.mojo.bindings.test.mojom.ping.PingService.PingResponse;
import org.chromium.mojo.bindings.test.mojom.sample.Factory;
import org.chromium.mojo.bindings.test.mojom.sample.NamedObject;
import org.chromium.mojo.bindings.test.mojom.sample.NamedObject.GetNameResponse;
......@@ -161,6 +163,37 @@ public class InterfacesTest extends MojoTestCase {
}
}
/**
* Implementation of PingService.
*/
public class PingServiceImpl extends CapturingErrorHandler implements PingService {
public void ping(PingResponse callback) {
callback.call();
}
/**
* @see org.chromium.mojo.bindings.Interface#close()
*/
@Override
public void close() {}
}
/**
* Implementation of {@link PingResponse} keeping track of usage.
*/
public static class RecordingPingResponse implements PingResponse {
private boolean mCalled;
@Override
public void call() {
mCalled = true;
}
public boolean wasCalled() {
return mCalled;
}
}
/**
* @see MojoTestCase#tearDown()
*/
......@@ -281,4 +314,43 @@ public class InterfacesTest extends MojoTestCase {
assertTrue(response.wasResponseCalled());
}
@SmallTest
public void testPingProxyAndStub() {
PingServiceImpl impl = new PingServiceImpl();
PingService.Proxy proxy =
PingService.MANAGER.buildProxy(null, PingService.MANAGER.buildStub(null, impl));
RecordingPingResponse callback = new RecordingPingResponse();
CapturingErrorHandler errorHandler = new CapturingErrorHandler();
proxy.getProxyHandler().setErrorHandler(errorHandler);
assertNull(impl.getLastMojoException());
proxy.ping(callback);
assertNull(errorHandler.getLastMojoException());
assertTrue(callback.wasCalled());
}
@SmallTest
public void testPingProxyAndStubOverPipe() {
PingServiceImpl impl = new PingServiceImpl();
PingService.Proxy proxy =
BindingsTestUtils.newProxyOverPipe(PingService.MANAGER, impl, mCloseablesToClose);
RecordingPingResponse callback = new RecordingPingResponse();
CapturingErrorHandler errorHandler = new CapturingErrorHandler();
proxy.getProxyHandler().setErrorHandler(errorHandler);
assertNull(impl.getLastMojoException());
proxy.ping(callback);
runLoopUntilIdle();
assertNull(errorHandler.getLastMojoException());
assertTrue(callback.wasCalled());
}
}
......@@ -9,6 +9,7 @@ component("common") {
output_name = "mojo_common_lib"
sources = [
"binding_set.h",
"common_type_converters.cc",
"common_type_converters.h",
"data_pipe_drainer.cc",
......@@ -27,7 +28,6 @@ component("common") {
"task_tracker.h",
"time_helper.cc",
"time_helper.h",
"weak_binding_set.h",
]
if (is_nacl) {
......@@ -46,13 +46,13 @@ component("common") {
test("mojo_common_unittests") {
sources = [
"binding_set_unittest.cc",
"common_type_converters_unittest.cc",
"data_pipe_utils_unittest.cc",
"handle_watcher_unittest.cc",
"interface_ptr_set_unittest.cc",
"message_pump_mojo_unittest.cc",
"task_tracker_unittest.cc",
"weak_binding_set_unittest.cc",
]
deps = [
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_COMMON_WEAK_BINDING_SET_H_
#define MOJO_COMMON_WEAK_BINDING_SET_H_
#ifndef MOJO_COMMON_BINDING_SET_H_
#define MOJO_COMMON_BINDING_SET_H_
#include <algorithm>
#include <vector>
......@@ -16,10 +16,10 @@ namespace mojo {
// Use this class to manage a set of bindings each of which is
// owned by the pipe it is bound to.
template <typename Interface>
class WeakBindingSet {
class BindingSet {
public:
WeakBindingSet() {}
~WeakBindingSet() { CloseAllBindings(); }
BindingSet() {}
~BindingSet() { CloseAllBindings(); }
void AddBinding(Interface* impl, InterfaceRequest<Interface> request) {
bindings_.emplace_back(new Binding<Interface>(impl, request.Pass()));
......@@ -37,18 +37,16 @@ class WeakBindingSet {
});
}
void CloseAllBindings() {
bindings_.clear();
}
void CloseAllBindings() { bindings_.clear(); }
size_t size() const { return bindings_.size(); }
private:
std::vector<std::unique_ptr<Binding<Interface>>> bindings_;
DISALLOW_COPY_AND_ASSIGN(WeakBindingSet);
DISALLOW_COPY_AND_ASSIGN(BindingSet);
};
} // namespace mojo
#endif // MOJO_COMMON_WEAK_BINDING_SET_H_
#endif // MOJO_COMMON_BINDING_SET_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/weak_binding_set.h"
#include "mojo/common/binding_set.h"
#include "base/message_loop/message_loop.h"
#include "mojo/common/message_pump_mojo.h"
......@@ -29,8 +29,8 @@ class DummyImpl : public tests::Dummy {
DISALLOW_COPY_AND_ASSIGN(DummyImpl);
};
// Tests all of the functionality of WeakBindingSet.
TEST(WeakBindingSet, FullLifeCycleTest) {
// Tests all of the functionality of BindingSet.
TEST(BindingSetTest, FullLifeCycle) {
base::MessageLoop loop(MessagePumpMojo::Create());
// Create 10 InterfacePtrs and DummyImpls.
......@@ -40,7 +40,7 @@ TEST(WeakBindingSet, FullLifeCycleTest) {
// Create 10 message pipes, bind everything together, and add the
// bindings to binding_set.
WeakBindingSet<tests::Dummy> binding_set;
BindingSet<tests::Dummy> binding_set;
EXPECT_EQ(0u, binding_set.size());
for (size_t i = 0; i < kNumObjects; i++) {
binding_set.AddBinding(&impls[i], GetProxy(&intrfc_ptrs[i]));
......
......@@ -16,8 +16,12 @@ dartzip_package("apptest") {
}
dart_pkg("apptest_pkg") {
entrypoints = [ "lib/apptest.dart" ]
sources = [
"lib/apptest.dart",
"pubspec.yaml",
]
deps = [
"//mojo/public/dart:mojo",
"//third_party/dart-pkg",
]
}
......@@ -10,17 +10,17 @@ import 'package:mojo/application.dart';
import 'package:mojo/bindings.dart';
import 'package:mojo/core.dart';
// Import and reexport the unittest package. We are a *.dartzip file designed to
// Import and reexport the test package. We are a *.dartzip file designed to
// be linked into your_apptest.mojo file and are your main entrypoint.
import 'package:unittest/unittest.dart';
export 'package:unittest/unittest.dart';
import 'package:test/test.dart';
export 'package:test/test.dart';
final Completer exitCodeCompleter = new Completer();
typedef AppTestFunction(Application app, String url);
// This class is an application that does nothing but tears down the connections
// between each test.
class _ConnectionToShellApplication extends Application {
final List<Function> _testFunctions;
final List<AppTestFunction> _testFunctions;
_ConnectionToShellApplication.fromHandle(
MojoHandle handle, this._testFunctions)
......@@ -30,53 +30,39 @@ class _ConnectionToShellApplication extends Application {
// call from the shell. We need to first have a valid connection to the shell
// so that apptests can connect to other applications.
void initialize(List<String> args, String url) {
_testFunctions.forEach((f) => f(this, url));
group('dart_apptests', () {
setUp(testSetUp);
tearDown(testTearDown);
for (var testFunction in _testFunctions) {
testFunction(this, url);
}
});
// Append a final test to terminate shell connection.
// TODO(johnmccutchan): Remove this once package 'test' supports a global
// tearDown callback.
test('TERMINATE SHELL CONNECTION', () async {
await close();
assert(MojoHandle.reportLeakedHandles());
});
}
}
// A configuration which properly shuts down our application at the end.
class _CleanShutdownConfiguration extends SimpleConfiguration {
final _ConnectionToShellApplication _application;
_CleanShutdownConfiguration(this._application) : super() {}
Duration timeout = const Duration(seconds: 10);
void onTestResult(TestCase externalTestCase) {
super.onTestResult(externalTestCase);
_application.resetConnections();
void testSetUp() {
}
void onDone(bool success) {
exitCodeCompleter.complete(success ? 0 : 255);
closeApplication();
super.onDone(success);
}
Future closeApplication() async {
await _application.close();
assert(MojoHandle.reportLeakedHandles());
}
void onSummary(int passed, int failed, int errors,
List<TestCase> results, String uncaughtError) {
String status = ((failed > 0) || (errors > 0)) ? "FAILED" : "PASSED";
print('DART APPTESTS RESULT: $status');
super.onSummary(passed, failed, errors, results, uncaughtError);
void testTearDown() {
// Reset any connections between tests.
resetConnections();
}
}
// The public interface to apptests.
//
// In a dart mojo application, |incoming_handle| is args[0]. |testFunction| is a
// list of functions that actually contains your testing code, and will pass
// back an application to each of them.
Future<int> runAppTests(var incomingHandle, List<Function> testFunction) async {
/// The public interface to apptests.
///
/// In a dart mojo application, [incomingHandle] is `args[0]`. [testFunctions]
/// is list of [AppTestFunction]. Each function will be passed the application
/// and url.
runAppTests(var incomingHandle, List<AppTestFunction> testFunctions) {
var appHandle = new MojoHandle(incomingHandle);
var application =
new _ConnectionToShellApplication.fromHandle(appHandle, testFunction);
unittestConfiguration = new _CleanShutdownConfiguration(application);
var exitCode = await exitCodeCompleter.future;
return exitCode;
new _ConnectionToShellApplication.fromHandle(appHandle, testFunctions);
/// [Application]'s [initialize] will be called.
}
# Generated by pub
# See http://pub.dartlang.org/doc/glossary.html#lockfile
packages:
analyzer:
description: analyzer
source: hosted
version: "0.25.2"
args:
description: args
source: hosted
version: "0.13.2"
async:
description: async
source: hosted
version: "1.2.0"
barback:
description: barback
source: hosted
version: "0.15.2+4"
charcode:
description: charcode
source: hosted
version: "1.1.0"
collection:
description: collection
source: hosted
version: "1.1.1"
crypto:
description: crypto
source: hosted
version: "0.9.0"
csslib:
description: csslib
source: hosted
version: "0.12.1"
glob:
description: glob
source: hosted
version: "1.0.5"
html:
description: html
source: hosted
version: "0.12.1+2"
http_multi_server:
description: http_multi_server
source: hosted
version: "1.3.2"
http_parser:
description: http_parser
source: hosted
version: "0.0.2+8"
logging:
description: logging
source: hosted
version: "0.11.1"
matcher:
description: matcher
source: hosted
version: "0.11.4+4"
version: "0.12.0+1"
mime:
description: mime
source: hosted
version: "0.9.3"
package_config:
description: package_config
source: hosted
version: "0.1.1"
path:
description: path
source: hosted
version: "1.3.4"
version: "1.3.6"
plugin:
description: plugin
source: hosted
version: "0.1.0"
pool:
description: pool
source: hosted
version: "1.1.0"
pub_semver:
description: pub_semver
source: hosted
version: "1.2.1"
shelf:
description: shelf
source: hosted
version: "0.6.2"
shelf_static:
description: shelf_static
source: hosted
version: "0.2.2"
shelf_web_socket:
description: shelf_web_socket
source: hosted
version: "0.0.1+2"
source_map_stack_trace:
description: source_map_stack_trace
source: hosted
version: "1.0.4"
source_maps:
description: source_maps
source: hosted
version: "0.10.1"
source_span:
description: source_span
source: hosted
version: "1.1.2"
stack_trace:
description: stack_trace
source: hosted
version: "1.2.4"
unittest:
description: unittest
version: "1.3.4"
string_scanner:
description: string_scanner
source: hosted
version: "0.1.3+1"
test:
description: test
source: hosted
version: "0.12.3+7"
utf:
description: utf
source: hosted
version: "0.9.0+2"
watcher:
description: watcher
source: hosted
version: "0.9.7"
yaml:
description: yaml
source: hosted
version: "0.11.5+4"
version: "2.1.3"
name: apptest
version: 0.0.1
dependencies:
unittest: '>=0.11.5+4 <0.12.0'
test: ^0.12.3+7
......@@ -16,15 +16,11 @@ source_set("dart_controller_no_snapshot") {
"common.h",
"dart_controller.cc",
"dart_controller.h",
"dart_debugger.cc",
"dart_debugger.h",
"io/internet_address.h",
"isolate_data.h",
"mojo_io_natives.cc",
"mojo_io_natives.h",
"mojo_natives.cc",
"mojo_natives.h",
"monitor.h",
"vmservice.cc",
"vmservice.h",
]
......@@ -46,6 +42,7 @@ source_set("dart_controller_no_snapshot") {
"//dart/runtime:libdart",
"//dart/runtime/bin:libdart_embedder_noio",
"//third_party/dart-pkg",
"//mojo/common",
"//mojo/public/c/system",
"//mojo/public/cpp/system",
]
......@@ -56,8 +53,6 @@ source_set("dart_controller_no_snapshot") {
} else {
defines += [ "NDEBUG" ]
}
include_dirs = [ "//dart/runtime" ]
}
dart_embedder_resources("generate_dart_embedder_patch_resources_cc") {
......
......@@ -8,8 +8,9 @@ import 'dart:async';
import 'dart:convert';
import 'dart:mojo.internal';
// import 'root_library'; happens here from C Code
const _logBuiltin = false;
// import 'root_library'; happens here from C Code
// The root library (aka the script) is imported into this library. The
// embedder uses this to lookup the main entrypoint in the root library's
// namespace.
......@@ -26,26 +27,23 @@ class _Logger {
_getPrintClosure() => _print;
const _logBuiltin = false;
Uri _uriBase() {
return _entryPointScript.resolve('.');
}
_getUriBaseClosure() => _uriBase;
// The current working directory
var _workingDirectoryUri;
// The URI that the entry point script was loaded from. Remembered so that
// package imports can be resolved relative to it.
var _entryPointScript;
// The directory to look in to resolve "package:" scheme URIs.
var _packageRoot;
_getUriBaseClosure() => _uriBase;
_setupHooks() {
VMLibraryHooks.eventHandlerSendData = MojoHandleWatcher.timer;
}
_enforceTrailingSlash(uri) {
Uri _workingDirectory;
Uri _entryPointScript;
Uri _packageRoot;
_enforceTrailingSlash(String uri) {
// Ensure we have a trailing slash character.
if (!uri.endsWith('/')) {
return '$uri/';
......@@ -53,31 +51,30 @@ _enforceTrailingSlash(uri) {
return uri;
}
void _setWorkingDirectory(cwd) {
_setWorkingDirectory(String cwd) {
cwd = _enforceTrailingSlash(cwd);
_workingDirectoryUri = new Uri(scheme: 'file', path: cwd);
_workingDirectory = new Uri(scheme: 'file', path: cwd);
if (_logBuiltin) {
_print('# Working Directory: $cwd');
_print('# Working Directory: $_workingDirectory');
}
}
_setPackageRoot(String packageRoot) {
packageRoot = _enforceTrailingSlash(packageRoot);
if (packageRoot.startsWith('file:') ||
packageRoot.startsWith('http:') ||
packageRoot.startsWith('https:')) {
_packageRoot = _workingDirectoryUri.resolve(packageRoot);
_packageRoot = _workingDirectory.resolve(packageRoot);
} else {
_packageRoot = _workingDirectoryUri.resolveUri(new Uri.file(packageRoot));
_packageRoot = _workingDirectory.resolveUri(new Uri.file(packageRoot));
}
if (_logBuiltin) {
_print('# Package root: $packageRoot -> $_packageRoot');
}
}
String _resolveScriptUri(String scriptName) {
if (_workingDirectoryUri == null) {
_resolveScriptUri(String scriptName) {
if (_workingDirectory == null) {
throw 'No current working directory set.';
}
......@@ -88,147 +85,9 @@ String _resolveScriptUri(String scriptName) {
} else {
// Script does not have a scheme, assume that it is a path,
// resolve it against the working directory.
_entryPointScript = _workingDirectoryUri.resolve(scriptName);
}
if (_logBuiltin) {
_print('# Resolved entry point to: $_entryPointScript');
}
return _entryPointScript.toString();
}
const _DART_EXT = 'dart-ext:';
String _resolveUri(String base, String userString) {
if (_logBuiltin) {
_print('# Resolving: $userString from $base');
}
var baseUri = Uri.parse(base);
if (userString.startsWith(_DART_EXT)) {
var uri = userString.substring(_DART_EXT.length);
return '$_DART_EXT${baseUri.resolve(uri)}';
} else {
return baseUri.resolve(userString).toString();
}
}
Uri _resolvePackageUri(Uri uri) {
if (!uri.host.isEmpty) {
var path = '${uri.host}${uri.path}';
var right = 'package:$path';
var wrong = 'package://$path';
throw "URIs using the 'package:' scheme should look like "
"'$right', not '$wrong'.";
}
var packageRoot = _packageRoot == null
? _entryPointScript.resolve('packages/')
: _packageRoot;
return packageRoot.resolve(uri.path);
}
int _numOutstandingLoadRequests = 0;
// TODO(zra): Enable loading libraries over http.
// void _httpGet(Uri uri, String libraryUri, loadCallback(List<int> data)) {
// }
void _signalDoneLoading() native "Builtin_DoneLoading";
void _loadScriptCallback(int tag, String uri, String libraryUri,
List<int> data) native "Builtin_LoadScript";
void _loadScript(int tag, String uri, String libraryUri, List<int> data) {
_loadScriptCallback(tag, uri, libraryUri, data);
assert(_numOutstandingLoadRequests > 0);
_numOutstandingLoadRequests--;
if (_logBuiltin) {
_print("native Builtin_LoadScript($uri) completed, "
"${_numOutstandingLoadRequests} requests remaining");
}
if (_numOutstandingLoadRequests == 0) {
_signalDoneLoading();
}
}
void _asyncLoadErrorCallback(
uri, libraryUri, error) native "Builtin_AsyncLoadError";
void _asyncLoadError(uri, libraryUri, error) {
assert(_numOutstandingLoadRequests > 0);
if (_logBuiltin) {
_print("_asyncLoadError($uri), error: $error");
}
_numOutstandingLoadRequests--;
_asyncLoadErrorCallback(uri, libraryUri, error);
if (_numOutstandingLoadRequests == 0) {
_signalDoneLoading();
}
}
// Create a Uri of 'userUri'. If the input uri is a package uri, then the
// package uri is resolved.
Uri _createUri(String userUri) {
var uri = Uri.parse(userUri);
if (_logBuiltin) {
_print('# Creating uri for: $uri');
_entryPointScript = _workingDirectory.resolve(scriptName);
}
// TODO(zra): Except for the special handling for package:, URI's should just
// be sent to the network stack to resolve.
switch (uri.scheme) {
case '':
case 'file':
case 'http':
case 'https':
return uri;
case 'package':
return _resolvePackageUri(uri);
default:
// Only handling file, http[s], and package URIs
// in standalone binary.
if (_logBuiltin) {
_print('# Unknown scheme (${uri.scheme}) in $uri.');
}
throw 'Not a known scheme: $uri';
}
}
// TODO(zra): readSync and enumerateFiles are exposed for testing purposes only.
// Eventually, there will be different builtin libraries for testing and
// production(i.e. the content handler). In the content handler's builtin
// library, File IO capabilities will be removed.
// This uses the synchronous base::ReadFileToString exposed by Mojo.
List<int> readSync(String uri) native "Builtin_ReadSync";
// This uses base::FileEnumerator.
List<String> enumerateFiles(String path) native "Builtin_EnumerateFiles";
// Asynchronously loads script data through a http[s] or file uri.
_loadDataAsync(int tag, String uri, String libraryUri, List<int> source) {
if (tag == null) {
uri = _resolveScriptUri(uri);
}
Uri resourceUri = _createUri(uri);
_numOutstandingLoadRequests++;
if (_logBuiltin) {
_print("_loadDataAsync($uri), "
"${_numOutstandingLoadRequests} requests outstanding");
}
if (source != null) {
_loadScript(tag, uri, libraryUri, source);
return;
}
if ((resourceUri.scheme == 'http') || (resourceUri.scheme == 'https')) {
// TODO(zra): Enable library loading over http.
// _httpGet(resourceUri, libraryUri, (data) {
// _loadScript(tag, uri, libraryUri, data);
// });
throw 'Cannot load http, yet.';
} else {
// Mojo does not expose any asynchronous file IO calls, but we'll maintain
// the same structure as the standalone embedder here in case it ever does.
var data = readSync(resourceUri.toFilePath());
_loadScript(tag, uri, libraryUri, data);
_print('# Script entry point: $scriptName -> $_entryPointScript');
}
}
......@@ -29,11 +29,6 @@ namespace dart {
#define BUILTIN_NATIVE_LIST(V) \
V(Crypto_GetRandomBytes, 1) \
V(Logger_PrintString, 1) \
V(Builtin_ReadSync, 1) \
V(Builtin_EnumerateFiles, 1) \
V(Builtin_LoadScript, 4) \
V(Builtin_AsyncLoadError, 3) \
V(Builtin_DoneLoading, 0)
BUILTIN_NATIVE_LIST(DECLARE_FUNCTION);
......@@ -100,201 +95,6 @@ void Logger_PrintString(Dart_NativeArguments args) {
fflush(stdout);
}
void Builtin_ReadSync(Dart_NativeArguments args) {
intptr_t chars_length = 0;
uint8_t* chars = nullptr;
Dart_Handle file_uri = Dart_GetNativeArgument(args, 0);
if (!Dart_IsString(file_uri)) {
Dart_PropagateError(file_uri);
}
intptr_t file_uri_length = 0;
Dart_Handle result = Dart_StringLength(file_uri, &file_uri_length);
result = Dart_StringToUTF8(file_uri, &chars, &chars_length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
chars[file_uri_length] = '\0';
base::FilePath path(base::FilePath::FromUTF8Unsafe(
std::string(reinterpret_cast<char*>(chars))));
std::string source;
if (ReadFileToString(path, &source)) {
Dart_Handle data =
Dart_NewTypedData(Dart_TypedData_kUint8, source.length());
char* source_chars = const_cast<char*>(source.c_str());
Dart_ListSetAsBytes(data, 0, reinterpret_cast<uint8_t*>(source_chars),
source.length());
Dart_SetReturnValue(args, data);
return;
}
LOG(ERROR) << "Failed to read path: " << chars;
Dart_SetReturnValue(args, Dart_Null());
}
void Builtin_EnumerateFiles(Dart_NativeArguments args) {
intptr_t chars_length = 0;
uint8_t* chars = nullptr;
Dart_Handle path = Dart_GetNativeArgument(args, 0);
if (!Dart_IsString(path)) {
Dart_PropagateError(path);
}
intptr_t path_length = 0;
Dart_Handle result = Dart_StringLength(path, &path_length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result = Dart_StringToUTF8(path, &chars, &chars_length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
chars[path_length] = '\0';
base::FilePath dir_path(base::FilePath::FromUTF8Unsafe(
std::string(reinterpret_cast<char*>(chars))));
std::vector<std::string> entries;
base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES);
while (true) {
base::FilePath file_path = enumerator.Next();
std::string file_path_string = file_path.AsUTF8Unsafe();
if (file_path_string == "") {
break;
}
entries.push_back(file_path_string);
}
Dart_Handle list = Dart_NewList(entries.size());
for (uintptr_t i = 0; i < entries.size(); i++) {
Dart_Handle entry = Dart_NewStringFromUTF8(
reinterpret_cast<const uint8_t*>(entries[i].data()),
entries[i].length());
Dart_ListSetAt(list, i, entry);
}
Dart_SetReturnValue(args, list);
}
// Callback function, gets called from asynchronous script and library
// reading code when there is an i/o error.
void Builtin_AsyncLoadError(Dart_NativeArguments args) {
// Dart_Handle source_uri = Dart_GetNativeArgument(args, 0);
Dart_Handle library_uri = Dart_GetNativeArgument(args, 1);
Dart_Handle error = Dart_GetNativeArgument(args, 2);
Dart_Handle library = Dart_LookupLibrary(library_uri);
// If a library with the given uri exists, give it a chance to handle
// the error. If the load requests stems from a deferred library load,
// an IO error is not fatal.
if (!Dart_IsError(library)) {
DCHECK(Dart_IsLibrary(library));
Dart_Handle res = Dart_LibraryHandleError(library, error);
if (Dart_IsNull(res)) {
return;
}
}
// The error was not handled above. Propagate an unhandled exception.
error = Dart_NewUnhandledExceptionError(error);
Dart_PropagateError(error);
}
static const uint8_t* SniffForMagicNumber(const uint8_t* text_buffer,
intptr_t* buffer_len,
bool* is_snapshot) {
intptr_t len = sizeof(Builtin::snapshot_magic_number);
for (intptr_t i = 0; i < len; i++) {
if (text_buffer[i] != Builtin::snapshot_magic_number[i]) {
*is_snapshot = false;
return text_buffer;
}
}
*is_snapshot = true;
DCHECK_GT(*buffer_len, len);
*buffer_len -= len;
return text_buffer + len;
}
// Callback function called when the asynchronous load request of a
// script, library or part is complete.
void Builtin_LoadScript(Dart_NativeArguments args) {
Dart_Handle tag_in = Dart_GetNativeArgument(args, 0);
Dart_Handle resolved_script_uri = Dart_GetNativeArgument(args, 1);
Dart_Handle library_uri = Dart_GetNativeArgument(args, 2);
Dart_Handle source_data = Dart_GetNativeArgument(args, 3);
Dart_TypedData_Type type = Dart_GetTypeOfExternalTypedData(source_data);
bool external = type == Dart_TypedData_kUint8;
uint8_t* data = nullptr;
intptr_t num_bytes;
Dart_Handle result = Dart_TypedDataAcquireData(
source_data, &type, reinterpret_cast<void**>(&data), &num_bytes);
if (Dart_IsError(result))
Dart_PropagateError(result);
scoped_ptr<uint8_t[]> buffer_copy;
if (!external) {
// If the buffer is not external, take a copy.
buffer_copy.reset(new uint8_t[num_bytes]);
memmove(buffer_copy.get(), data, num_bytes);
data = buffer_copy.get();
}
Dart_TypedDataReleaseData(source_data);
if (Dart_IsNull(tag_in) && Dart_IsNull(library_uri)) {
// No tag and a null library_uri indicates this is
// a top-level script, check if it is a snapshot or a regular
// script file and load accordingly.
bool is_snapshot = false;
const uint8_t* payload =
SniffForMagicNumber(data, &num_bytes, &is_snapshot);
if (is_snapshot) {
result = Dart_LoadScriptFromSnapshot(payload, num_bytes);
} else {
Dart_Handle source = Dart_NewStringFromUTF8(data, num_bytes);
if (Dart_IsError(source)) {
result = Builtin::NewError("%s is not a valid UTF-8 script",
resolved_script_uri);
} else {
result = Dart_LoadScript(resolved_script_uri, source, 0, 0);
}
}
} else {
int64_t tag = Builtin::GetIntegerValue(tag_in);
Dart_Handle source = Dart_NewStringFromUTF8(data, num_bytes);
if (Dart_IsError(source)) {
result = Builtin::NewError("%s is not a valid UTF-8 script",
resolved_script_uri);
} else {
if (tag == Dart_kImportTag) {
result = Dart_LoadLibrary(resolved_script_uri, source, 0, 0);
} else {
DCHECK_EQ(tag, Dart_kSourceTag);
Dart_Handle library = Dart_LookupLibrary(library_uri);
DART_CHECK_VALID(library);
result = Dart_LoadSource(library, resolved_script_uri, source, 0, 0);
}
}
}
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
}
// Callback function that gets called from dartutils when there are
// no more outstanding load requests.
void Builtin_DoneLoading(Dart_NativeArguments args) {
Dart_Handle res = Dart_FinalizeLoading(true);
if (Dart_IsError(res)) {
Dart_PropagateError(res);
}
}
static bool GetInt64Value(Dart_Handle value_obj, int64_t* value) {
bool valid = Dart_IsInteger(value_obj);
if (valid) {
......
......@@ -5,27 +5,70 @@
#ifndef MOJO_DART_EMBEDDER_DART_CONTROLLER_H_
#define MOJO_DART_EMBEDDER_DART_CONTROLLER_H_
#include <unordered_set>
#include "base/synchronization/lock.h"
#include "dart/runtime/include/dart_api.h"
#include "mojo/dart/embedder/isolate_data.h"
#include "mojo/dart/embedder/dart_state.h"
#include "mojo/public/c/system/types.h"
namespace tonic {
class DartDependency;
class DartLibraryLoader;
}
namespace mojo {
namespace dart {
struct DartControllerConfig {
DartControllerConfig()
: application_data(nullptr),
strict_compilation(false),
entropy(nullptr),
vm_flags(nullptr),
vm_flags_count(0),
script_flags(nullptr),
script_flags_count(0),
handle(MOJO_HANDLE_INVALID),
compile_all(false),
error(nullptr),
use_network_loader(false) {
}
void SetVmFlags(const char** vm_flags, intptr_t vm_flags_count) {
this->vm_flags = vm_flags;
this->vm_flags_count = vm_flags_count;
// See if compile_all is one of the vm flags.
compile_all = false;
const char* kCompileAllFlag = "--compile_all";
const intptr_t kCompileAllFlagLen = strlen(kCompileAllFlag);
for (intptr_t i = 0; i < vm_flags_count; i++) {
if (strncmp(vm_flags[i], kCompileAllFlag, kCompileAllFlagLen) == 0) {
compile_all = true;
}
}
}
void SetScriptFlags(const char** script_flags, intptr_t script_flags_count) {
this->script_flags = script_flags;
this->script_flags_count = script_flags_count;
}
void* application_data;
bool strict_compilation;
std::string script;
std::string script_uri;
std::string package_root;
IsolateCallbacks callbacks;
Dart_EntropySource entropy;
const char** arguments;
int arguments_count;
const char** vm_flags;
int vm_flags_count;
const char** script_flags;
int script_flags_count;
MojoHandle handle;
// TODO(zra): search for the --compile_all flag in arguments where needed.
bool compile_all;
char** error;
bool use_network_loader;
};
// The DartController may need to request for services to be connected
......@@ -104,14 +147,26 @@ class DartController {
static Dart_Isolate CreateIsolateHelper(void* dart_app,
bool strict_compilation,
IsolateCallbacks callbacks,
const std::string& script,
const std::string& script_uri,
const std::string& package_root,
char** error);
char** error,
bool use_network_loader);
static void InitVmIfNeeded(Dart_EntropySource entropy,
const char** arguments,
int arguments_count);
static void BlockWaitingForDependencies(
tonic::DartLibraryLoader* loader,
const std::unordered_set<tonic::DartDependency*>& dependencies);
static void LoadEmptyScript(const std::string& script_uri);
static void InnerLoadScript(const std::string& script_uri,
tonic::DartLibraryProvider* library_provider);
static void LoadScript(const std::string& script_uri,
tonic::DartLibraryProvider* library_provider);
static tonic::DartLibraryProvider* library_provider_;
static base::Lock lock_;
static bool initialized_;
static bool strict_compilation_;
static bool service_isolate_running_;
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/dart/embedder/dart_debugger.h"
#include "dart/runtime/include/dart_api.h"
#include "dart/runtime/include/dart_native_api.h"
#include "dart/runtime/include/dart_tools_api.h"
namespace mojo {
namespace dart {
void DartDebuggerIsolate::MessageLoop() {
MonitorLocker ml(&monitor_);
// Request notification on isolate messages. This allows us to
// respond to vm service messages while at breakpoint.
Dart_SetMessageNotifyCallback(DartDebugger::NotifyIsolate);
while (true) {
// Handle all available vm service messages, up to a resume
// request.
bool resume = false;
while (!resume && Dart_HasServiceMessages()) {
monitor_.Exit();
resume = Dart_HandleServiceMessages();
monitor_.Enter();
}
if (resume) {
break;
}
ml.Wait();
}
Dart_SetMessageNotifyCallback(nullptr);
}
void DartDebugger::BptResolvedHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
// Nothing to do here. Service event is dispatched to let Observatory know
// that a breakpoint was resolved.
}
void DartDebugger::PausedEventHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& loc) {
Dart_EnterScope();
intptr_t isolate_index = FindIsolateIndexById(isolate_id);
CHECK(isolate_index != -1);
(*isolates_)[isolate_index]->MessageLoop();
Dart_ExitScope();
}
void DartDebugger::ExceptionThrownHandler(Dart_IsolateId isolate_id,
Dart_Handle exception,
Dart_StackTrace stack_trace) {
Dart_EnterScope();
intptr_t isolate_index = FindIsolateIndexById(isolate_id);
CHECK(isolate_index != -1);
(*isolates_)[isolate_index]->MessageLoop();
Dart_ExitScope();
}
void DartDebugger::IsolateEventHandler(Dart_IsolateId isolate_id,
Dart_IsolateEvent kind) {
Dart_EnterScope();
if (kind == Dart_IsolateEvent::kCreated) {
AddIsolate(isolate_id);
} else {
intptr_t isolate_index = FindIsolateIndexById(isolate_id);
CHECK(isolate_index != -1);
if (kind == Dart_IsolateEvent::kInterrupted) {
(*isolates_)[isolate_index]->MessageLoop();
} else {
CHECK(kind == Dart_IsolateEvent::kShutdown);
RemoveIsolate(isolate_id);
}
}
Dart_ExitScope();
}
void DartDebugger::NotifyIsolate(Dart_Isolate isolate) {
base::AutoLock al(*lock_);
Dart_IsolateId isolate_id = Dart_GetIsolateId(isolate);
intptr_t isolate_index = FindIsolateIndexByIdLocked(isolate_id);
if (isolate_index >= 0) {
(*isolates_)[isolate_index]->Notify();
}
}
void DartDebugger::InitDebugger() {
Dart_SetIsolateEventHandler(IsolateEventHandler);
Dart_SetPausedEventHandler(PausedEventHandler);
Dart_SetBreakpointResolvedHandler(BptResolvedHandler);
Dart_SetExceptionThrownHandler(ExceptionThrownHandler);
lock_ = new base::Lock();
isolates_ = new std::vector<std::unique_ptr<DartDebuggerIsolate>>();
}
intptr_t DartDebugger::FindIsolateIndexById(Dart_IsolateId id) {
base::AutoLock al(*lock_);
return FindIsolateIndexByIdLocked(id);
}
intptr_t DartDebugger::FindIsolateIndexByIdLocked(
Dart_IsolateId id) {
lock_->AssertAcquired();
for (size_t i = 0; i < isolates_->size(); i++) {
if ((*isolates_)[i]->id() == id) {
return i;
}
}
return -1;
}
void DartDebugger::AddIsolate(Dart_IsolateId id) {
base::AutoLock al(*lock_);
CHECK(FindIsolateIndexByIdLocked(id) == -1);
std::unique_ptr<DartDebuggerIsolate> debugger_isolate =
std::unique_ptr<DartDebuggerIsolate>(new DartDebuggerIsolate(id));
isolates_->push_back(std::move(debugger_isolate));
}
void DartDebugger::RemoveIsolate(Dart_IsolateId id) {
base::AutoLock al(*lock_);
for (size_t i = 0; i < isolates_->size(); i++) {
if (id == (*isolates_)[i]->id()) {
isolates_->erase(isolates_->begin() + i);
return;
}
}
NOTREACHED();
}
base::Lock* DartDebugger::lock_ = nullptr;
std::vector<std::unique_ptr<DartDebuggerIsolate>>* DartDebugger::isolates_ =
nullptr;
} // namespace dart
} // namespace mojo
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
#define MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
#include <memory>
#include <vector>
#include "dart/runtime/include/dart_api.h"
#include "dart/runtime/include/dart_native_api.h"
#include "dart/runtime/include/dart_tools_api.h"
#include "mojo/dart/embedder/monitor.h"
namespace base {
class Lock;
}
namespace mojo {
namespace dart {
class DartDebuggerIsolate {
public:
DartDebuggerIsolate(Dart_IsolateId id)
: id_(id) {
}
Dart_IsolateId id() const {
return id_;
}
void Notify() {
monitor_.Notify();
}
void MessageLoop();
private:
const Dart_IsolateId id_;
Monitor monitor_;
};
class DartDebugger {
public:
static void InitDebugger();
private:
static void BptResolvedHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location);
static void PausedEventHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& loc);
static void ExceptionThrownHandler(Dart_IsolateId isolate_id,
Dart_Handle exception,
Dart_StackTrace stack_trace);
static void IsolateEventHandler(Dart_IsolateId isolate_id,
Dart_IsolateEvent kind);
static void NotifyIsolate(Dart_Isolate isolate);
static intptr_t FindIsolateIndexById(Dart_IsolateId id);
static intptr_t FindIsolateIndexByIdLocked(Dart_IsolateId id);
static void AddIsolate(Dart_IsolateId id);
static void RemoveIsolate(Dart_IsolateId id);
static base::Lock* lock_;
static std::vector<std::unique_ptr<DartDebuggerIsolate>>* isolates_;
friend class DartDebuggerIsolate;
};
} // namespace dart
} // namespace mojo
#endif // MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
\ No newline at end of file
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_DART_EMBEDDER_DART_STATE_H_
#define MOJO_DART_EMBEDDER_DART_STATE_H_
#include <set>
#include <string>
#include "base/callback.h"
#include "base/macros.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "tonic/dart_library_provider.h"
#include "tonic/dart_state.h"
namespace mojo {
namespace dart {
struct IsolateCallbacks {
base::Callback<Dart_Isolate(const char*,const char*,const char*,void*,char**)>
create;
base::Callback<void(void*)> shutdown;
base::Callback<void(Dart_Handle)> exception;
};
// State associated with an isolate (retrieved via |Dart_CurrentIsolateData|).
class MojoDartState : public tonic::DartState {
public:
MojoDartState(void* application_data,
bool strict_compilation,
IsolateCallbacks callbacks,
std::string script_uri,
std::string package_root)
: application_data_(application_data),
strict_compilation_(strict_compilation),
callbacks_(callbacks),
script_uri_(script_uri),
package_root_(package_root),
library_provider_(nullptr) {
}
void* application_data() const { return application_data_; }
bool strict_compilation() const { return strict_compilation_; }
const IsolateCallbacks& callbacks() const { return callbacks_; }
const std::string& script_uri() const { return script_uri_; }
const std::string& package_root() const { return package_root_; }
std::set<MojoHandle>& unclosed_handles() {
return unclosed_handles_;
}
const std::set<MojoHandle>& unclosed_handles() const {
return unclosed_handles_;
}
void set_library_provider(tonic::DartLibraryProvider* library_provider) {
library_provider_.reset(library_provider);
DCHECK(library_provider_.get() == library_provider);
}
// Takes ownership of |raw_handle|.
void BindNetworkService(MojoHandle raw_handle) {
if (raw_handle == MOJO_HANDLE_INVALID) {
return;
}
DCHECK(!network_service_.is_bound());
MessagePipeHandle handle(raw_handle);
ScopedMessagePipeHandle message_pipe(handle);
InterfacePtrInfo<mojo::NetworkService> interface_info(message_pipe.Pass(),
0);
network_service_.Bind(interface_info.Pass());
DCHECK(network_service_.is_bound());
}
mojo::NetworkService* network_service() {
// Should only be called after |BindNetworkService|.
DCHECK(network_service_.is_bound());
return network_service_.get();
}
tonic::DartLibraryProvider* library_provider() const {
return library_provider_.get();
}
static MojoDartState* From(Dart_Isolate isolate) {
return reinterpret_cast<MojoDartState*>(DartState::From(isolate));
}
static MojoDartState* Current() {
return reinterpret_cast<MojoDartState*>(DartState::Current());
}
static MojoDartState* Cast(void* data) {
return reinterpret_cast<MojoDartState*>(data);
}
private:
void* application_data_;
bool strict_compilation_;
IsolateCallbacks callbacks_;
std::string script_uri_;
std::string package_root_;
std::set<MojoHandle> unclosed_handles_;
std::unique_ptr<tonic::DartLibraryProvider> library_provider_;
mojo::NetworkServicePtr network_service_;
};
} // namespace dart
} // namespace mojo
#endif // MOJO_DART_EMBEDDER_DART_STATE_H_
\ No newline at end of file
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_DART_EMBEDDER_ISOLATE_DATA_H_
#define MOJO_DART_EMBEDDER_ISOLATE_DATA_H_
#include <set>
#include <string>
#include "base/callback.h"
#include "base/macros.h"
#include "dart/runtime/include/dart_api.h"
#include "mojo/public/c/system/types.h"
namespace mojo {
namespace dart {
struct IsolateCallbacks {
base::Callback<Dart_Isolate(const char*,const char*,const char*,void*,char**)>
create;
base::Callback<void(void*)> shutdown;
base::Callback<void(Dart_Handle)> exception;
};
class IsolateData {
public:
IsolateData(void* app,
bool strict_compilation,
IsolateCallbacks callbacks,
const std::string& script,
const std::string& script_uri,
const std::string& package_root)
: app(app),
strict_compilation(strict_compilation),
callbacks(callbacks),
script(script),
script_uri(script_uri),
package_root(package_root) {}
void* app;
bool strict_compilation;
IsolateCallbacks callbacks;
std::string script;
std::string script_uri;
std::string package_root;
std::set<MojoHandle> unclosed_handles;
DISALLOW_COPY_AND_ASSIGN(IsolateData);
};
} // namespace dart
} // namespace mojo
#endif // MOJO_DART_EMBEDDER_ISOLATE_DATA_H_
......@@ -10,8 +10,9 @@
#include "dart/runtime/include/dart_api.h"
#include "mojo/dart/embedder/builtin.h"
#include "mojo/dart/embedder/common.h"
#include "mojo/dart/embedder/dart_state.h"
#include "mojo/dart/embedder/io/internet_address.h"
#include "mojo/dart/embedder/isolate_data.h"
namespace mojo {
namespace dart {
......@@ -157,15 +158,11 @@ void Platform_ExecutableArguments(Dart_NativeArguments arguments) {
}
void Platform_PackageRoot(Dart_NativeArguments arguments) {
const char* package_root = "";
Dart_Isolate isolate = Dart_CurrentIsolate();
DCHECK(isolate != nullptr);
void* data = Dart_IsolateData(isolate);
IsolateData* isolate_data = reinterpret_cast<IsolateData*>(data);
if (isolate_data != nullptr) {
package_root = isolate_data->package_root.c_str();
}
Dart_SetReturnValue(arguments, Dart_NewStringFromCString(package_root));
auto isolate_data = MojoDartState::Current();
DCHECK(isolate_data != nullptr);
Dart_SetReturnValue(
arguments,
Dart_NewStringFromCString(isolate_data->package_root().c_str()));
}
void Platform_GetVersion(Dart_NativeArguments arguments) {
......
......@@ -12,7 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "dart/runtime/include/dart_api.h"
#include "mojo/dart/embedder/builtin.h"
#include "mojo/dart/embedder/isolate_data.h"
#include "mojo/dart/embedder/dart_state.h"
#include "mojo/public/c/system/core.h"
#include "mojo/public/cpp/system/core.h"
......@@ -149,10 +149,9 @@ void MojoHandle_Register(Dart_NativeArguments arguments) {
// Add the handle to this isolate's set.
MojoHandle handle = static_cast<MojoHandle>(raw_handle);
Dart_Isolate isolate = Dart_CurrentIsolate();
void* data = Dart_IsolateData(isolate);
IsolateData* isolate_data = reinterpret_cast<IsolateData*>(data);
isolate_data->unclosed_handles.insert(handle);
auto isolate_data = MojoDartState::Current();
DCHECK(isolate_data != nullptr);
isolate_data->unclosed_handles().insert(handle);
// Set up a finalizer.
CloserCallbackPeer* callback_peer = new CloserCallbackPeer();
......@@ -170,10 +169,9 @@ void MojoHandle_Close(Dart_NativeArguments arguments) {
// Remove the handle from this isolate's set.
MojoHandle handle = static_cast<MojoHandle>(raw_handle);
Dart_Isolate isolate = Dart_CurrentIsolate();
void* data = Dart_IsolateData(isolate);
IsolateData* isolate_data = reinterpret_cast<IsolateData*>(data);
isolate_data->unclosed_handles.erase(handle);
auto isolate_data = MojoDartState::Current();
DCHECK(isolate_data != nullptr);
isolate_data->unclosed_handles().erase(handle);
MojoResult res = MojoClose(handle);
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_DART_EMBEDDER_MONITOR_H_
#define MOJO_DART_EMBEDDER_MONITOR_H_
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
namespace mojo {
namespace dart {
class Monitor {
public:
Monitor() {
lock_ = new base::Lock();
condition_variable_ = new base::ConditionVariable(lock_);
}
~Monitor() {
delete condition_variable_;
delete lock_;
}
void Enter() {
lock_->Acquire();
}
void Exit() {
lock_->Release();
}
void Notify() {
condition_variable_->Signal();
}
void Wait() {
condition_variable_->Wait();
}
private:
base::Lock* lock_;
base::ConditionVariable* condition_variable_;
DISALLOW_COPY_AND_ASSIGN(Monitor);
};
class MonitorLocker {
public:
explicit MonitorLocker(Monitor* monitor) : monitor_(monitor) {
CHECK(monitor_);
monitor_->Enter();
}
virtual ~MonitorLocker() {
monitor_->Exit();
}
void Wait() {
return monitor_->Wait();
}
void Notify() {
monitor_->Notify();
}
private:
Monitor* const monitor_;
DISALLOW_COPY_AND_ASSIGN(MonitorLocker);
};
} // namespace dart
} // namespace mojo
#endif // MOJO_DART_EMBEDDER_MONITOR_H_
......@@ -296,10 +296,6 @@ class DartToCppTest : public testing::Test {
.AppendASCII("test")
.AppendASCII(test);
// Read in the source.
std::string source;
EXPECT_TRUE(ReadFileToString(path, &source)) << "Failed to read test file";
// Setup the package root.
base::FilePath package_root;
PathService::Get(base::DIR_EXE, &package_root);
......@@ -309,17 +305,13 @@ class DartToCppTest : public testing::Test {
config->strict_compilation = true;
config->script = source;
config->script_uri = path.AsUTF8Unsafe();
config->package_root = package_root.AsUTF8Unsafe();
config->application_data = nullptr;
config->callbacks.exception =
base::Bind(&UnhandledExceptionCallback, unhandled_exception);
config->entropy = GenerateEntropy;
config->handle = handle;
config->arguments = arguments;
config->arguments_count = arguments_count;
config->compile_all = false;
config->SetVmFlags(arguments, arguments_count);
config->error = error;
}
......
......@@ -108,7 +108,7 @@ class DartSideImpl implements DartSide {
}
main(List args) {
assert(args.length == 2);
assert(args.length == 3);
int mojoHandle = args[0];
var rawHandle = new core.MojoHandle(mojoHandle);
var endpoint = new core.MojoMessagePipeEndpoint(rawHandle);
......
......@@ -29,7 +29,6 @@ static void exceptionCallback(bool* exception, Dart_Handle error) {
}
static void RunTest(const std::string& test,
bool compile_all,
const char** extra_args,
int num_extra_args) {
base::FilePath path;
......@@ -39,10 +38,6 @@ static void RunTest(const std::string& test,
.AppendASCII("test")
.AppendASCII(test);
// Read in the source.
std::string source;
EXPECT_TRUE(ReadFileToString(path, &source)) << "Failed to read test file";
// Setup the package root.
base::FilePath package_root;
PathService::Get(base::DIR_EXE, &package_root);
......@@ -56,17 +51,12 @@ static void RunTest(const std::string& test,
// Run with strict compilation even in Release mode so that ASAN testing gets
// coverage of Dart asserts, type-checking, etc.
config.strict_compilation = true;
config.script = source;
config.script_uri = path.AsUTF8Unsafe();
config.package_root = package_root.AsUTF8Unsafe();
config.application_data = nullptr;
config.callbacks.exception =
base::Bind(&exceptionCallback, &unhandled_exception);
config.entropy = generateEntropy;
config.arguments = extra_args;
config.arguments_count = num_extra_args;
config.handle = MOJO_HANDLE_INVALID;
config.compile_all = compile_all;
config.SetVmFlags(extra_args, num_extra_args);
config.error = &error;
bool success = DartController::RunSingleDartScript(config);
......@@ -78,71 +68,72 @@ static void RunTest(const std::string& test,
// _test.dart files.
TEST(DartTest, hello_mojo) {
RunTest("hello_mojo.dart", false, nullptr, 0);
RunTest("hello_mojo.dart", nullptr, 0);
}
TEST(DartTest, core_types_test) {
RunTest("core_types_test.dart", false, nullptr, 0);
RunTest("core_types_test.dart", nullptr, 0);
}
TEST(DartTest, async_test) {
RunTest("async_test.dart", false, nullptr, 0);
RunTest("async_test.dart", nullptr, 0);
}
TEST(DartTest, isolate_test) {
RunTest("isolate_test.dart", false, nullptr, 0);
RunTest("isolate_test.dart", nullptr, 0);
}
TEST(DartTest, import_mojo) {
RunTest("import_mojo.dart", false, nullptr, 0);
RunTest("import_mojo.dart", nullptr, 0);
}
TEST(DartTest, simple_handle_watcher_test) {
RunTest("simple_handle_watcher_test.dart", false, nullptr, 0);
RunTest("simple_handle_watcher_test.dart", nullptr, 0);
}
TEST(DartTest, ping_pong_test) {
RunTest("ping_pong_test.dart", false, nullptr, 0);
RunTest("ping_pong_test.dart", nullptr, 0);
}
TEST(DartTest, timer_test) {
RunTest("timer_test.dart", false, nullptr, 0);
RunTest("timer_test.dart", nullptr, 0);
}
TEST(DartTest, async_await_test) {
RunTest("async_await_test.dart", false, nullptr, 0);
RunTest("async_await_test.dart", nullptr, 0);
}
TEST(DartTest, core_test) {
RunTest("core_test.dart", false, nullptr, 0);
RunTest("core_test.dart", nullptr, 0);
}
TEST(DartTest, codec_test) {
RunTest("codec_test.dart", false, nullptr, 0);
RunTest("codec_test.dart", nullptr, 0);
}
TEST(DartTest, handle_watcher_test) {
RunTest("handle_watcher_test.dart", false, nullptr, 0);
RunTest("handle_watcher_test.dart", nullptr, 0);
}
TEST(DartTest, bindings_generation_test) {
RunTest("bindings_generation_test.dart", false, nullptr, 0);
RunTest("bindings_generation_test.dart", nullptr, 0);
}
TEST(DartTest, compile_all_interfaces_test) {
RunTest("compile_all_interfaces_test.dart", true, nullptr, 0);
const char* args[] = { "--compile_all" };
RunTest("compile_all_interfaces_test.dart", &args[0], 1);
}
TEST(DartTest, uri_base_test) {
RunTest("uri_base_test.dart", false, nullptr, 0);
RunTest("uri_base_test.dart", nullptr, 0);
}
TEST(DartTest, exception_test) {
RunTest("exception_test.dart", false, nullptr, 0);
RunTest("exception_test.dart", nullptr, 0);
}
TEST(DartTest, control_messages_test) {
RunTest("control_messages_test.dart", false, nullptr, 0);
RunTest("control_messages_test.dart", nullptr, 0);
}
TEST(DartTest, handle_finalizer_test) {
......@@ -150,7 +141,7 @@ TEST(DartTest, handle_finalizer_test) {
const char* args[kNumArgs];
args[0] = "--new-gen-semi-max-size=1";
args[1] = "--old_gen_growth_rate=1";
RunTest("handle_finalizer_test.dart", false, args, kNumArgs);
RunTest("handle_finalizer_test.dart", args, kNumArgs);
}
} // namespace
......
......@@ -9,11 +9,13 @@
#include <vector>
#include "base/bind.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "mojo/dart/embedder/dart_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -44,7 +46,64 @@ static void exceptionCallback(bool* exception, Dart_Handle error) {
*exception = true;
}
// Enumerates files inside |path| and collects all data needed to run
// conformance tests.
// For each test there are three entries in the returned vector.
// [0] -> test name.
// [1] -> contents of test's .data file.
// [2] -> contents of test's .expected file.
static std::vector<std::string> CollectTests(base::FilePath path) {
base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES);
std::set<std::string> tests;
while (true) {
base::FilePath file_path = enumerator.Next();
if (file_path.empty()) {
break;
}
file_path = file_path.RemoveExtension();
tests.insert(file_path.value());
}
std::vector<std::string> result;
for (auto it = tests.begin(); it != tests.end(); it++) {
const std::string& test_name = *it;
std::string source;
bool r;
std::string filename = base::FilePath(test_name).BaseName().value();
if (!StartsWithASCII(filename, "conformance_", true)) {
// Only include conformance tests.
continue;
}
base::FilePath data_path(test_name);
data_path = data_path.AddExtension(".data");
base::FilePath expected_path(test_name);
expected_path = expected_path.AddExtension(".expected");
// Test name.
result.push_back(test_name.c_str());
// Test's .data.
r = ReadFileToString(data_path, &source);
DCHECK(r);
result.push_back(source);
source.clear();
// Test's .expected.
r = ReadFileToString(expected_path, &source);
DCHECK(r);
result.push_back(source);
source.clear();
}
return result;
}
static void RunTest(const std::string& test) {
// Gather test data.
std::vector<std::string> arguments = CollectTests(base::FilePath(test));
DCHECK(arguments.size() > 0);
// Grab the C string pointer.
std::vector<const char*> arguments_c_str;
for (size_t i = 0; i < arguments.size(); i++) {
arguments_c_str.push_back(arguments[i].c_str());
}
DCHECK(arguments.size() == arguments_c_str.size());
base::FilePath path;
PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.AppendASCII("mojo")
......@@ -52,10 +111,6 @@ static void RunTest(const std::string& test) {
.AppendASCII("test")
.AppendASCII("validation_test.dart");
// Read in the source.
std::string source;
EXPECT_TRUE(ReadFileToString(path, &source)) << "Failed to read test file";
// Setup the package root.
base::FilePath package_root;
PathService::Get(base::DIR_EXE, &package_root);
......@@ -66,21 +121,17 @@ static void RunTest(const std::string& test) {
char* error = NULL;
bool unhandled_exception = false;
DartControllerConfig config;
config.script = source;
// Run with strict compilation even in Release mode so that ASAN testing gets
// coverage of Dart asserts, type-checking, etc.
config.strict_compilation = true;
config.script_uri = test;
config.script_uri = path.value();
config.package_root = package_root.AsUTF8Unsafe();
config.application_data = nullptr;
config.callbacks.exception =
base::Bind(&exceptionCallback, &unhandled_exception);
config.entropy = generateEntropy;
config.arguments = nullptr;
config.arguments_count = 0;
config.handle = MOJO_HANDLE_INVALID;
config.compile_all = false;
config.SetVmFlags(nullptr, 0);
config.error = &error;
config.SetScriptFlags(arguments_c_str.data(), arguments_c_str.size());
bool success = DartController::RunSingleDartScript(config);
EXPECT_TRUE(success) << error;
......
#!mojo mojo:dart_content_handler
library test;
import 'dart:async';
import 'dart:mojo.internal';
part 'part0.dart';
main(List args) {
// Hang around for a sec and then close the shell handle.
new Timer(new Duration(milliseconds: 1000), () {
print('PASS');
MojoHandleNatives.close(args[0]);
});
}
part of test;
String get foo => 'FOO';
#!/usr/bin/python
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This script runs the Dart content handler http load test in the mojo tree."""
import argparse
import os
import subprocess
import sys
MOJO_SHELL = 'mojo_shell'
def main(build_dir, dart_exe, tester_script, tester_dir):
shell_exe = os.path.join(build_dir, MOJO_SHELL)
subprocess.check_call([
dart_exe,
tester_script,
shell_exe,
tester_dir,
])
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="List filenames of files in the packages/ subdir of the "
"given directory.")
parser.add_argument("--build-dir",
dest="build_dir",
metavar="<build-directory>",
type=str,
required=True,
help="The directory containing mojo_shell.")
parser.add_argument("--dart-exe",
dest="dart_exe",
metavar="<package-name>",
type=str,
required=True,
help="Path to dart executable.")
args = parser.parse_args()
tester_dir = os.path.dirname(os.path.realpath(__file__))
tester_dart_script = os.path.join(tester_dir, 'tester.dart');
sys.exit(main(args.build_dir, args.dart_exe, tester_dart_script, tester_dir))
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library http_load_test;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
class Launcher {
/// Launch [executable] with [arguments]. Returns a [String] containing
/// the standard output from the launched executable.
static Future<String> launch(String executable,
List<String> arguments) async {
var process = await Process.start(executable, arguments);
// Completer completes once the child process exits.
var completer = new Completer();
String stdout = '';
process.stdout.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
stdout = '$stdout\n$line';
print(line);
});
process.stderr.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
print(line);
});
process.exitCode.then((ec) {
stdout = '$stdout\nEXIT_CODE=$ec\n';
completer.complete(stdout);
});
return completer.future;
}
}
main(List<String> args) async {
var mojo_shell_executable = args[0];
var directory = args[1];
HttpServer server = await HttpServer.bind('127.0.0.1', 0);
server.listen((HttpRequest request) async {
final String path = request.uri.toFilePath();
final File file = new File('${directory}/${path}');
if (await file.exists()) {
try {
await file.openRead().pipe(request.response);
} catch (e) {
print(e);
}
} else {
request.response.statusCode = HttpStatus.NOT_FOUND;
request.response.close();
}
});
var launchUrl = 'http://127.0.0.1:${server.port}/main.dart';
var stdout = await Launcher.launch(mojo_shell_executable, [launchUrl]);
server.close();
if (!stdout.contains("\nPASS")) {
throw "Test failed.";
}
if (!stdout.contains("\nEXIT_CODE=0\n")) {
throw "Test failed.";
}
}
......@@ -2,4 +2,4 @@ author: Chromium Authors <mojo-dev@googlegroups.com>
description: Generated bindings for mojo services
homepage: https://github.com/domokit/mojo
name: mojo_services
version: 0.0.16
version: 0.0.17
......@@ -5,10 +5,14 @@
import("//mojo/public/dart/rules.gni")
dart_pkg("mojom") {
entrypoints = [ "lib/generate.dart" ]
sources = [
"CHANGELOG.md",
"README.md",
"lib/generate.dart",
"lib/src/utils.dart",
"pubspec.yaml",
]
deps = [
"//third_party/dart-pkg",
]
}
......@@ -12,77 +12,24 @@
/// [-a additional-dirs]
/// [-m mojo-sdk]
/// [-g] # Generate from .mojom files
/// [-d] # Download from .mojoms files
/// [-v] # verbose
/// [-d] # Dry run
/// [-f] # Fake (dry) run
library generate;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart' as args;
import 'package:path/path.dart' as path;
part 'src/utils.dart';
bool verbose;
bool dryRun;
bool isMojomDart(String path) => path.endsWith('.mojom.dart');
bool isMojom(String path) => path.endsWith('.mojom');
/// An Error for problems on the command line.
class CommandLineError extends Error {
final _msg;
CommandLineError(this._msg);
toString() => _msg;
}
/// An Error for failures of the bindings generation script.
class GenerationError extends Error {
final _msg;
GenerationError(this._msg);
toString() => _msg;
}
/// The base type of data passed to actions for [mojomDirIter].
class PackageIterData {
final Directory _mojomPackage;
PackageIterData(this._mojomPackage);
Directory get mojomPackage => _mojomPackage;
}
/// Data for [mojomDirIter] that includes the path to the Mojo SDK for bindings
/// generation.
class GenerateIterData extends PackageIterData {
final Directory _mojoSdk;
GenerateIterData(this._mojoSdk, Directory mojomPackage)
: super(mojomPackage);
Directory get mojoSdk => _mojoSdk;
}
/// The type of action performed by [mojomDirIter].
typedef Future MojomAction(PackageIterData data, Directory mojomDirectory);
/// Iterates over mojom directories of Dart packages, taking some action for
/// each.
///
/// For each 'mojom' subdirectory of each subdirectory in [packages], runs
/// [action] on the subdirectory passing along [data] to [action].
mojomDirIter(
Directory packages, PackageIterData data, MojomAction action) async {
await for (var package in packages.list()) {
if (package is Directory) {
if (package.path == data.mojomPackage.path) continue;
if (verbose) print("package = $package");
final mojomDirectory = new Directory(path.join(package.path, 'mojom'));
if (verbose) print("looking for = $mojomDirectory");
if (await mojomDirectory.exists()) {
await action(data, mojomDirectory);
} else if (verbose) {
print("$mojomDirectory not found");
}
}
}
}
/// Searches for .mojom.dart files under [mojomDirectory] and copies them to
/// the 'mojom' packages.
copyAction(PackageIterData data, Directory mojomDirectory) async {
......@@ -108,7 +55,6 @@ copyAction(PackageIterData data, Directory mojomDirectory) async {
}
}
/// Searches for .mojom files under [mojomDirectory], generates .mojom.dart
/// files for them, and copies them to the 'mojom' package.
generateAction(GenerateIterData data, Directory mojomDirectory) async {
......@@ -118,14 +64,16 @@ generateAction(GenerateIterData data, Directory mojomDirectory) async {
if (verbose) print("Found $mojom");
final script = path.join(data.mojoSdk.path,
'mojo', 'public', 'tools', 'bindings', 'mojom_bindings_generator.py');
'tools', 'bindings', 'mojom_bindings_generator.py');
final sdkInc = path.normalize(path.join(data.mojoSdk.path, '..', '..'));
final outputDir = await data.mojomPackage.createTemp();
final output = outputDir.path;
final arguments = [
'--use_bundled_pylibs',
'-g', 'dart',
'-o', output,
// TODO(zra): Are other include paths needed?
'-I', data.mojoSdk.path,
'-I', sdkInc,
'-I', mojomDirectory.path,
mojom.path];
......@@ -150,10 +98,77 @@ generateAction(GenerateIterData data, Directory mojomDirectory) async {
}
}
/// In each package, look for a file named .mojoms. Populate a package's
/// mojom directory with the downloaded mojoms, creating the directory if
/// needed. The .mojoms file should be formatted as follows:
/// '''
/// root: https://www.example.com/mojoms
/// path/to/some/mojom1.mojom
/// path/to/some/other/mojom2.mojom
///
/// root: https://www.example-two.com/mojoms
/// path/to/example/two/mojom1.mojom
/// ...
///
/// Lines beginning with '#' are ignored.
downloadAction(GenerateIterData data, Directory packageDirectory) async {
var mojomsPath = path.join(packageDirectory.path, '.mojoms');
var mojomsFile = new File(mojomsPath);
if (!await mojomsFile.exists()) return;
if (verbose) print("Found .mojoms file: $mojomsPath");
Directory mojomsDir;
var httpClient = new HttpClient();
int repoCount = 0;
int mojomCount = 0;
String repoRoot;
for (String line in await mojomsFile.readAsLines()) {
line = line.trim();
if (line.isEmpty || line.startsWith('#')) continue;
if (line.startsWith('root:')) {
if ((mojomsDir != null) && (mojomCount == 0)) {
throw new DownloadError("root with no mojoms: $repoRoot");
}
mojomCount = 0;
var rootWords = line.split(" ");
if (rootWords.length != 2) {
throw new DownloadError("Malformed root: $line");
}
repoRoot = rootWords[1];
if (verbose) print("Found repo root: $repoRoot");
if (!repoRoot.startsWith('http://') &&
!repoRoot.startsWith('https://')) {
throw new DownloadError(
'Mojom repo "root" should be an http or https URL: $line');
}
mojomsDir = new Directory(path.join(
packageDirectory.parent.path, 'mojm.repo.$repoCount', 'mojom'));
await mojomsDir.create(recursive: true);
repoCount++;
} else {
if (mojomsDir == null) {
throw new DownloadError('Malformed .mojoms file: $mojomsPath');
}
String url = "$repoRoot/$line";
if (verbose) print("Found $url");
String fileString = await getUrl(httpClient, url);
if (verbose) print("Downloaded $url");
String filePath = path.join(mojomsDir.path, line);
var file = new File(filePath);
if (!await file.exists()) {
await file.create(recursive: true);
await file.writeAsString(fileString);
if (verbose) print("Wrote $filePath");
}
mojomCount++;
}
}
}
/// Ensures that the directories in [additionalPaths] are absolute and exist,
/// and creates Directories for them, which are returned.
validateAdditionalDirs(Iterable additionalPaths) async {
Future<List<Directory>> validateAdditionalDirs(Iterable additionalPaths) async {
var additionalDirs = [];
for (var mojomPath in additionalPaths) {
final mojomDir = new Directory(mojomPath);
......@@ -171,19 +186,35 @@ validateAdditionalDirs(Iterable additionalPaths) async {
return additionalDirs;
}
class GenerateOptions {
final Directory packages;
final Directory mojomPackage;
final Directory mojoSdk;
final List<Directory> additionalDirs;
final bool download;
final bool generate;
GenerateOptions(
this.packages, this.mojomPackage, this.mojoSdk, this.additionalDirs,
this.download, this.generate);
}
main(List<String> arguments) async {
Future<GenerateOptions> parseArguments(List<String> arguments) async {
final parser = new args.ArgParser()
..addOption('additional-mojom-dir',
abbr: 'a',
allowMultiple: true,
help: 'Absolute path to an additional directory containing mojom.dart'
'files to put in the mojom package. May be specified multiple times.')
..addFlag('dry-run',
..addFlag('download',
abbr: 'd',
defaultsTo: false,
help: 'Print the copy operations that would have been run, but'
'do not copy anything.')
help: 'Searches packages for a .mojoms file, and downloads .mojom files'
'as speficied in that file. Implies -g.')
..addFlag('fake',
abbr: 'f',
defaultsTo: false,
help: 'Print the operations that would have been run, but'
'do not run anything.')
..addFlag('generate',
abbr: 'g',
defaultsTo: false,
......@@ -200,7 +231,7 @@ main(List<String> arguments) async {
..addFlag('verbose', abbr: 'v', defaultsTo: false);
final result = parser.parse(arguments);
verbose = result['verbose'];
dryRun = result['dry-run'];
dryRun = result['fake'];
final packages = new Directory(result['package-root']);
if (!packages.isAbsolute) {
......@@ -220,7 +251,8 @@ main(List<String> arguments) async {
"The mojom package directory $mojomPackage must exist");
}
final generate = result['generate'];
final download = result['download'];
final generate = result['generate'] || download;
var mojoSdk = null;
if (generate) {
final mojoSdkPath = result['mojo-sdk'];
......@@ -237,18 +269,41 @@ main(List<String> arguments) async {
}
}
await mojomDirIter(packages, new PackageIterData(mojomPackage), copyAction);
if (generate) {
await mojomDirIter(packages, new GenerateIterData(mojoSdk, mojomPackage),
generateAction);
}
final additionalDirs =
await validateAdditionalDirs(result['additional-mojom-dir']);
final data = new GenerateIterData(mojoSdk, mojomPackage);
for (var mojomDir in additionalDirs) {
return new GenerateOptions(
packages, mojomPackage, mojoSdk, additionalDirs, download, generate);
}
main(List<String> arguments) async {
var options = await parseArguments(arguments);
// Copy any pregenerated files form packages.
await mojomDirIter(
options.packages,
new PackageIterData(options.mojomPackage),
copyAction);
// Download .mojom files. These will be picked up by the generation step
// below.
if (options.download) {
await packageDirIter(options.packages, null, downloadAction);
}
// Generate mojom files.
if (options.generate) {
await mojomDirIter(
options.packages,
new GenerateIterData(options.mojoSdk, options.mojomPackage),
generateAction);
}
// Copy pregenerated files from specified external directories.
final data = new GenerateIterData(options.mojoSdk, options.mojomPackage);
for (var mojomDir in options.additionalDirs) {
await copyAction(data, mojomDir);
if (generate) {
if (options.generate) {
await generateAction(data, mojomDir);
}
}
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of generate;
bool isMojomDart(String path) => path.endsWith('.mojom.dart');
bool isMojom(String path) => path.endsWith('.mojom');
/// An Error for problems on the command line.
class CommandLineError extends Error {
final _msg;
CommandLineError(this._msg);
toString() => _msg;
}
/// An Error for failures of the bindings generation script.
class GenerationError extends Error {
final _msg;
GenerationError(this._msg);
toString() => _msg;
}
/// An Error for failing to download a .mojom file.
class DownloadError extends Error {
final _msg;
DownloadError(this._msg);
toString() => _msg;
}
/// The base type of data passed to actions for [mojomDirIter].
class PackageIterData {
final Directory _mojomPackage;
PackageIterData(this._mojomPackage);
Directory get mojomPackage => _mojomPackage;
}
/// Data for [mojomDirIter] that includes the path to the Mojo SDK for bindings
/// generation.
class GenerateIterData extends PackageIterData {
final Directory _mojoSdk;
GenerateIterData(this._mojoSdk, Directory mojomPackage)
: super(mojomPackage);
Directory get mojoSdk => _mojoSdk;
}
/// The type of action performed by [mojomDirIter].
typedef Future MojomAction(PackageIterData data, Directory mojomDirectory);
packageDirIter(
Directory packages, PackageIterData data, MojomAction action) async {
await for (var package in packages.list()) {
if (package is Directory) {
await action(data, package);
}
}
}
/// Iterates over mojom directories of Dart packages, taking some action for
/// each.
///
/// For each 'mojom' subdirectory of each subdirectory in [packages], runs
/// [action] on the subdirectory passing along [data] to [action].
mojomDirIter(
Directory packages, PackageIterData data, MojomAction action) async {
await packageDirIter(packages, data, (d, p) async {
if (p.path == d.mojomPackage.path) return;
if (verbose) print("package = $p");
final mojomDirectory = new Directory(path.join(p.path, 'mojom'));
if (verbose) print("looking for = $mojomDirectory");
if (await mojomDirectory.exists()) {
await action(d, mojomDirectory);
} else if (verbose) {
print("$mojomDirectory not found");
}
});
}
/// Download file at [url] using [httpClient]. Throw a [DownloadError] if
/// the file is not successfully downloaded.
Future<String> getUrl(HttpClient httpClient, String url) async {
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode >= 400) {
var msg = "Failed to download $url\nCode ${response.statusCode}";
if (response.reasonPhrase != null) {
msg = "$msg: ${response.reasonPhrase}";
}
throw new DownloadError(msg);
}
var fileString = new StringBuffer();
await for (String contents in response.transform(UTF8.decoder)) {
fileString.write(contents);
}
return fileString.toString();
} catch(e) {
throw new DownloadError("$e");
}
}
......@@ -17,7 +17,34 @@ struct Transform {
};
''';
void main() {
final dldMojomContents1 = '''
module downloaded;
struct Downloaded1 {
int32 status;
};
''';
final dldMojomContents2 = '''
module downloaded;
struct Downloaded2 {
int32 status;
};
''';
main() async {
String mojoSdk;
if (Platform.environment['MOJO_SDK'] != null) {
mojoSdk = Platform.environment['MOJO_SDK'];
} else {
mojoSdk = path.normalize(path.join(
path.dirname(Platform.script.path), '..', '..', '..', 'public'));
}
if (!await new Directory(mojoSdk).exists()) {
fail("Could not find the Mojo SDK");
}
final scriptPath = path.dirname(Platform.script.path);
final testPackagePath = path.join(scriptPath, 'test_packages');
final testMojomPath = path.join(testPackagePath, 'mojom');
......@@ -30,6 +57,9 @@ void main() {
final additionalPath = path.join(
additionalRootPath, 'additional', 'additional.mojom.dart');
final downloadedPackagePath = path.join(testPackagePath, 'downloaded');
final dotMojomsPath = path.join(downloadedPackagePath, '.mojoms');
setUp(() async {
await new Directory(testMojomPath).create(recursive: true);
await new File(pregenFilePath).create(recursive: true);
......@@ -39,6 +69,8 @@ void main() {
'mojom', 'generated', 'public', 'interfaces', 'generated.mojom'));
await generatedMojomFile.create(recursive: true);
await generatedMojomFile.writeAsString(mojomContents);
await new Directory(downloadedPackagePath).create(recursive: true);
});
tearDown(() async {
......@@ -46,47 +78,148 @@ void main() {
await new Directory(testPackagePath).delete(recursive: true);
});
group('end-to-end', () {
group('No Download', () {
test('Copy', () async {
await generate.main(['-p', testPackagePath]);
await generate.main(['-p', testPackagePath, '-m', mojoSdk]);
final pregenFile = new File(
path.join(testMojomPath, 'pregen', 'pregen.mojom.dart'));
expect(await pregenFile.exists(), isTrue);
});
test('Copy and Additional', () async {
await generate.main(['-p', testPackagePath, '-a', additionalRootPath]);
await generate.main(['-p', testPackagePath, '-m', mojoSdk,
'-a', additionalRootPath]);
final additionalFile = new File(
path.join(testMojomPath, 'additional', 'additional.mojom.dart'));
expect(await additionalFile.exists(), isTrue);
});
test('Copy and Generate', () async {
if (Platform.environment['MOJO_SDK'] != null) {
await generate.main(['-g', '-p', testPackagePath]);
final generatedFile = new File(
path.join(testMojomPath, 'generated', 'generated.mojom.dart'));
expect(await generatedFile.exists(), isTrue);
}
await generate.main(['-g', '-p', testPackagePath, '-m', mojoSdk]);
final generatedFile = new File(
path.join(testMojomPath, 'generated', 'generated.mojom.dart'));
expect(await generatedFile.exists(), isTrue);
});
test('All', () async {
if (Platform.environment['MOJO_SDK'] != null) {
await generate.main([
'-g', '-p', testPackagePath, '-a', additionalRootPath]);
await generate.main([
'-g', '-p', testPackagePath, '-m', mojoSdk,
'-a', additionalRootPath]);
final pregenFile = new File(
path.join(testMojomPath, 'pregen', 'pregen.mojom.dart'));
expect(await pregenFile.exists(), isTrue);
final additionalFile = new File(
path.join(testMojomPath, 'additional', 'additional.mojom.dart'));
expect(await additionalFile.exists(), isTrue);
final generatedFile = new File(
path.join(testMojomPath, 'generated', 'generated.mojom.dart'));
expect(await generatedFile.exists(), isTrue);
});
});
group('Download', () {
var httpServer;
setUp(() async {
httpServer = await HttpServer.bind("localhost", 0);
httpServer.listen((HttpRequest request) {
String path = request.uri.path;
if (path.endsWith('path/to/mojom/download_one.mojom')) {
request.response.write(dldMojomContents1);
} else if (path.endsWith('path/to/mojom/download_two.mojom')) {
request.response.write(dldMojomContents2);
} else {
request.response.statusCode = HttpStatus.NOT_FOUND;
}
request.response.close();
});
});
tearDown(() async {
await httpServer.close();
httpServer = null;
});
final pregenFile = new File(
path.join(testMojomPath, 'pregen', 'pregen.mojom.dart'));
expect(await pregenFile.exists(), isTrue);
test('simple', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: http://localhost:${httpServer.port}\n"
"path/to/mojom/download_one.mojom\n");
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
final downloadedFile = new File(
path.join(testMojomPath, 'downloaded', 'download_one.mojom.dart'));
expect(await downloadedFile.exists(), isTrue);
await mojomsFile.delete();
});
final additionalFile = new File(
path.join(testMojomPath, 'additional', 'additional.mojom.dart'));
expect(await additionalFile.exists(), isTrue);
test('two files', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: http://localhost:${httpServer.port}\n"
"path/to/mojom/download_one.mojom\n"
"path/to/mojom/download_two.mojom\n");
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
final downloaded1File = new File(
path.join(testMojomPath, 'downloaded', 'download_one.mojom.dart'));
expect(await downloaded1File.exists(), isTrue);
final downloaded2File = new File(
path.join(testMojomPath, 'downloaded', 'download_two.mojom.dart'));
expect(await downloaded2File.exists(), isTrue);
await mojomsFile.delete();
});
final generatedFile = new File(
path.join(testMojomPath, 'generated', 'generated.mojom.dart'));
expect(await generatedFile.exists(), isTrue);
test('two roots', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: http://localhost:${httpServer.port}\n"
"path/to/mojom/download_one.mojom\n"
"root: http://localhost:${httpServer.port}\n"
"path/to/mojom/download_two.mojom\n");
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
final downloaded1File = new File(
path.join(testMojomPath, 'downloaded', 'download_one.mojom.dart'));
expect(await downloaded1File.exists(), isTrue);
final downloaded2File = new File(
path.join(testMojomPath, 'downloaded', 'download_two.mojom.dart'));
expect(await downloaded2File.exists(), isTrue);
await mojomsFile.delete();
});
test('simple-comment', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"# Comments are allowed\n"
" root: http://localhost:${httpServer.port}\n\n\n\n"
" # Here too\n"
" path/to/mojom/download_one.mojom\n"
"# And here\n");
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
final downloadedFile = new File(
path.join(testMojomPath, 'downloaded', 'download_one.mojom.dart'));
expect(await downloadedFile.exists(), isTrue);
await mojomsFile.delete();
});
test('404', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: http://localhost:${httpServer.port}\n"
"blah\n");
var fail = false;
try {
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
} on generate.DownloadError {
fail = true;
}
expect(fail, isTrue);
await mojomsFile.delete();
});
});
......@@ -95,7 +228,7 @@ void main() {
final dummyPackageRoot = path.join(scriptPath, 'dummyPackageRoot');
var fail = false;
try {
await generate.main(['-p', dummyPackageRoot]);
await generate.main(['-p', dummyPackageRoot, '-m', mojoSdk]);
} on generate.CommandLineError {
fail = true;
}
......@@ -106,7 +239,7 @@ void main() {
final dummyPackageRoot = 'dummyPackageRoot';
var fail = false;
try {
await generate.main(['-p', dummyPackageRoot]);
await generate.main(['-p', dummyPackageRoot, '-m', mojoSdk]);
} on generate.CommandLineError {
fail = true;
}
......@@ -117,7 +250,8 @@ void main() {
final dummyAdditional = path.join(scriptPath, 'dummyAdditional');
var fail = false;
try {
await generate.main(['-a', dummyAdditional, '-p', testPackagePath]);
await generate.main(['-a', dummyAdditional, '-p', testPackagePath,
'-m', mojoSdk]);
} on generate.CommandLineError {
fail = true;
}
......@@ -128,7 +262,8 @@ void main() {
final dummyAdditional = 'dummyAdditional';
var fail = false;
try {
await generate.main(['-a', dummyAdditional, '-p', testPackagePath]);
await generate.main(['-a', dummyAdditional, '-p', testPackagePath,
'-m', mojoSdk]);
} on generate.CommandLineError {
fail = true;
}
......@@ -142,7 +277,7 @@ void main() {
var fail = false;
try {
await generate.main(['-p', dummyPackageRoot]);
await generate.main(['-p', dummyPackageRoot, '-m', mojoSdk]);
} on generate.CommandLineError {
fail = true;
}
......@@ -160,5 +295,85 @@ void main() {
}
expect(fail, isTrue);
});
test('Download No Server', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: http://localhots\n"
"path/to/mojom/download_one.mojom\n");
var fail = false;
try {
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
} on generate.DownloadError {
fail = true;
}
expect(fail, isTrue);
await mojomsFile.delete();
});
test('.mojoms no root', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"path/to/mojom/download_one.mojom\n");
var fail = false;
try {
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
} on generate.DownloadError {
fail = true;
}
expect(fail, isTrue);
await mojomsFile.delete();
});
test('.mojoms blank root', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root:\n"
"path/to/mojom/download_one.mojom\n");
var fail = false;
try {
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
} on generate.DownloadError {
fail = true;
}
expect(fail, isTrue);
await mojomsFile.delete();
});
test('.mojoms root malformed', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: gobledygook\n"
"path/to/mojom/download_one.mojom\n");
var fail = false;
try {
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
} on generate.DownloadError {
fail = true;
}
expect(fail, isTrue);
await mojomsFile.delete();
});
test('.mojoms root without mojom', () async {
final mojomsFile = new File(dotMojomsPath);
await mojomsFile.create(recursive: true);
await mojomsFile.writeAsString(
"root: http://localhost\n"
"root: http://localhost\n"
"path/to/mojom/download_one.mojom\n");
var fail = false;
try {
await generate.main(['-p', testPackagePath, '-m', mojoSdk, '-d', '-g']);
} on generate.DownloadError {
fail = true;
}
expect(fail, isTrue);
await mojomsFile.delete();
});
});
}
......@@ -64,17 +64,6 @@ class ConformanceTestInterfaceImpl implements ConformanceTestInterface {
Future close({bool immediate: false}) => _stub.close(immediate: immediate);
}
parser.ValidationParseResult readAndParseTest(String test) {
List<int> data = builtin.readSync("${test}.data");
String input = new Utf8Decoder().convert(data).trim();
return parser.parse(input);
}
String expectedResult(String test) {
List<int> data = builtin.readSync("${test}.expected");
return new Utf8Decoder().convert(data).trim();
}
Future runTest(
String name, parser.ValidationParseResult test, String expected) {
var handles = new List.generate(
......@@ -112,26 +101,26 @@ Future runTest(
});
}
Iterable<String> getTestFiles(String path, String prefix) => builtin
.enumerateFiles(path)
.where((s) => s.startsWith(prefix) && s.endsWith(".data"))
.map((s) => s.replaceFirst('.data', ''));
main(List args) {
int handle = args[0];
String path = args[1];
main(List args) async {
assert(args.length == 3);
List<String> testData = args[2];
// First test the parser.
parser.parserTests();
// Then run the conformance tests.
var futures = getTestFiles(path, "$path/conformance_").map((test) {
return runTest(test, readAndParseTest(test), expectedResult(test));
});
Future.wait(futures).then((_) {
assert(MojoHandle.reportLeakedHandles());
}, onError: (e) {
assert(MojoHandle.reportLeakedHandles());
});
// TODO(zra): Add integration tests when they no longer rely on Client=.
// See CollectTests in validation_unittest.cc for information on testData
// format.
for (var i = 0; i < testData.length; i += 3) {
var name = testData[i + 0];
var data = testData[i + 1].trim();
var expected = testData[i + 2].trim();
try {
await runTest(name, parser.parse(data), expected);
print('$name PASSED.');
} catch (e) {
print('$name FAILED: $e');
}
}
// TODO(zra): Add integration tests when they no longer rely on Client=
assert(MojoHandle.reportLeakedHandles());
}
......@@ -5,10 +5,12 @@
import("//mojo/public/dart/rules.gni")
dart_pkg("testing") {
sources = [
entrypoints = [
"lib/async_helper.dart",
"lib/expect.dart",
"lib/validation_test_input_parser.dart",
]
sources = [
"pubspec.yaml",
]
}
......@@ -27,6 +27,7 @@ class ValidationParseError {
abstract class _Entry {
final int size;
_Entry(this.size);
void write(ByteData buffer, int offset, Map pointers);
}
......
# Devtools packages
Unopinionated tools for **running**, **debugging** and **testing** Mojo apps
available to Mojo consumers.
The `common` subdirectory contains what we currently expose as "devtools",
mirroring it as a [separate repository](https://github.com/domokit/devtools) for
consumption without a Mojo checkout.
Individual subdirectories are mirrored as separate repositories.
- **common** is the main toolset supporting mostly language-independent needs
on all Mojo platforms
- further subdirectories TBD might contain heavy language-specific tooling
## Principles
Further subdirectories TBD might be added in the future, to contain heavy
language-specific tooling which we will mirror / expose separately.
The toolsets are intended for consumption by Mojo consumers as **separate
checkouts**. No dependencies on files outside of devtools are allowed.
The toolsets should make no assumptions about particular **build system** or
**file layout** in any checkout where it is consumed. Consumers can add thin
wrappers adding a layer of convenience on top of them.
......@@ -12,17 +12,18 @@ git clone https://github.com/domokit/devtools.git
Devtools offers the following tools:
- `mojo_shell` - universal shell runner
- `debugger` - supports interactive tracing and debugging of a running mojo
shell
- `remote_adb_setup` - configures adb on a remote machine to communicate with
a device attached to the local machine
- `mojo_run` - shell runner
- `mojo_test` - apptest runner
- `mojo_debug` - debugger supporting interactive tracing and debugging of a
running mojo shell
and a Python scripting library designed for being embedded (`devtoolslib`).
Additionally, `remote_adb_setup` script helps to configure adb on a remote
machine to communicate with a device attached to a local machine, forwarding the
ports used by `mojo_run`.
### debugger
### Debugger
The `debugger` script allows you to interactively inspect a running shell,
The `mojo_debug` script allows you to interactively inspect a running shell,
collect performance traces and attach a gdb debugger.
#### Tracing
......@@ -31,29 +32,46 @@ traces](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)
and retrieve the result:
```sh
debugger tracing start
debugger tracing stop [result.json]
mojo_debug tracing start
mojo_debug tracing stop [result.json]
```
The trace file can be then loaded using the trace viewer in Chrome available at
`about://tracing`.
#### GDB
It is possible to inspect a Mojo Shell process using GDB. The `debugger` script
can be used to launch GDB and attach it to a running shell process (android
only):
It is possible to inspect a Mojo Shell process using GDB. The `mojo_debug`
script can be used to launch GDB and attach it to a running shell process
(android only):
```sh
debugger gdb attach
mojo_debug gdb attach
```
Once started, GDB will first stop the Mojo Shell execution, then load symbols
from loaded Mojo applications. Please note that this initial step can take some
time (up to several minutes in the worst case).
After each execution pause, GDB will update the set of loaded symbols based on
the selected thread only. If you need symbols for all threads, use the
`update-symbols` GDB command:
```sh
(gdb) update-symbols
```
If you only want to update symbols for the current selected thread (for example,
after changing threads), use the `current` option:
```sh
(gdb) update-symbols current
```
#### Android crash stacks
When Mojo shell crashes on Android ("Unfortunately, Mojo shell has stopped.")
due to a crash in native code, `debugger` can be used to find and symbolize the
stack trace present in the device log:
due to a crash in native code, `mojo_debug` can be used to find and symbolize
the stack trace present in the device log:
```sh
debugger device stack
mojo_debug device stack
```
### devtoolslib
......
......@@ -71,8 +71,7 @@ def _get_mapped_files():
class DebugSession(object):
def __init__(self, build_directory, package_name, pyelftools_dir=None,
adb='adb'):
def __init__(self, build_directory, package_name, pyelftools_dir, adb):
self._build_directory = build_directory
if not os.path.exists(self._build_directory):
logging.fatal("Please pass a valid build directory")
......@@ -86,8 +85,9 @@ class DebugSession(object):
try:
import elftools.elf.elffile as elffile
except ImportError:
logging.fatal("Unable to find elftools module; please install it "
"(for exmple, using 'pip install elftools')")
logging.fatal("Unable to find elftools module; please install pyelftools "
"and specify its path on the command line using "
"--pyelftools-dir.")
sys.exit(1)
self._elffile_module = elffile
......@@ -190,13 +190,33 @@ class DebugSession(object):
return True
return False
def _update_symbols(self):
def _map_symbols_on_current_thread(self, mapped_files):
"""Updates the symbols for the current thread using files from mapped_files.
"""
frame = gdb.newest_frame()
while frame and frame.is_valid():
if frame.name() is None:
m = self._find_mapping_for_address(mapped_files, frame.pc())
if m is not None and self._try_to_map(m):
# Force gdb to recompute its frames.
_gdb_execute("info threads")
frame = gdb.newest_frame()
assert frame.is_valid()
if (frame.older() is not None and
frame.older().is_valid() and
frame.older().pc() != frame.pc()):
frame = frame.older()
else:
frame = None
def update_symbols(self, current_thread_only):
"""Updates the mapping between symbols as seen from GDB and local library
files."""
files.
If current_thread_only is True, only update symbols for the current thread.
"""
logging.info("Updating symbols")
mapped_files = _get_mapped_files()
_gdb_execute("info threads")
nb_threads = len(_gdb_execute("info threads").split("\n")) - 2
# Map all symbols from native libraries packages with the APK.
for file_mappings in mapped_files:
filename = file_mappings[0].filename
......@@ -206,26 +226,20 @@ class DebugSession(object):
not filename.endswith('.dex')):
logging.info('Pre-mapping: %s' % file_mappings[0].filename)
self._try_to_map(file_mappings)
for i in xrange(nb_threads):
try:
_gdb_execute("thread %d" % (i + 1))
frame = gdb.newest_frame()
while frame and frame.is_valid():
if frame.name() is None:
m = self._find_mapping_for_address(mapped_files, frame.pc())
if m is not None and self._try_to_map(m):
# Force gdb to recompute its frames.
_gdb_execute("info threads")
frame = gdb.newest_frame()
assert frame.is_valid()
if (frame.older() is not None and
frame.older().is_valid() and
frame.older().pc() != frame.pc()):
frame = frame.older()
else:
frame = None
except gdb.error:
traceback.print_exc()
if current_thread_only:
self._map_symbols_on_current_thread(mapped_files)
else:
logging.info('Updating all threads\' symbols')
current_thread = gdb.selected_thread()
nb_threads = len(_gdb_execute("info threads").split("\n")) - 2
for i in xrange(nb_threads):
try:
_gdb_execute("thread %d" % (i + 1))
self._map_symbols_on_current_thread(mapped_files)
except gdb.error:
traceback.print_exc()
current_thread.switch()
def _get_device_application_pid(self, application):
"""Gets the PID of an application running on a device."""
......@@ -266,8 +280,38 @@ class DebugSession(object):
_gdb_execute('target remote localhost:9999')
self._update_symbols()
self.update_symbols(current_thread_only=False)
def on_stop(_):
self._update_symbols()
self.update_symbols(current_thread_only=True)
gdb.events.stop.connect(on_stop)
gdb.events.exited.connect(self.stop)
# Register the update-symbols command.
UpdateSymbols(self)
class UpdateSymbols(gdb.Command):
"""Command to update symbols loaded into GDB.
GDB usage: update-symbols [all|current]
"""
_UPDATE_COMMAND = "update-symbols"
def __init__(self, session):
super(UpdateSymbols, self).__init__(self._UPDATE_COMMAND, gdb.COMMAND_STACK)
self._session = session
def invoke(self, arg, _unused_from_tty):
if arg == 'current':
self._session.update_symbols(current_thread_only=True)
else:
self._session.update_symbols(current_thread_only=False)
def complete(self, text, _unused_word):
if text == self._UPDATE_COMMAND:
return ('all', 'current')
elif text in self._UPDATE_COMMAND + ' all':
return ['all']
elif text in self._UPDATE_COMMAND + ' current':
return ['current']
else:
return []
......@@ -6,14 +6,16 @@
/mojo/dart/apptests, built on top of the general apptest runner."""
import logging
import re
_logging = logging.getLogger()
from devtoolslib.apptest import run_apptest
SUCCESS_PATTERN = re.compile('^.+ .+: All tests passed!', re.MULTILINE)
def _dart_apptest_output_test(output):
return '\nDART APPTESTS RESULT: PASSED' in output
return SUCCESS_PATTERN.search(output) != None
# TODO(erg): Support android, launched services and fixture isolation.
......
......@@ -4,27 +4,8 @@
"""High-level apptest runner that runs all tests specified in a list.
The list of tests has to contain one dictionary per test to be run, in the
following form:
{
# Required URL for apptest.
"test": "mojo:test_app_url",
# Optional display name (otherwise the entry for "test" above is used).
"name": "mojo:test_app_url (more details)",
# Optional test type. Valid values:
# * "gtest" (default)
# * "gtest_isolated": like "gtest", but run with fixture isolation,
# i.e., each test in a fresh mojo_shell)
# * "dart".
"type": "gtest",
# Optional arguments for the apptest.
"test-args": ["--an_arg", "another_arg"],
# Optional arguments for the shell.
"shell-args": ["--some-flag-for-the-shell", "--another-flag"],
}
TODO(vtl|msw): Add a way of specifying data dependencies.
TODO(ppi): merge this into `mojo_test` once all clients are switched to use
`mojo_test` instead of calling run_apptests() directly.
"""
import sys
......@@ -44,7 +25,7 @@ def run_apptests(shell, common_shell_args, test_list):
common_shell_args: Arguments that will be passed to the shell on each run.
These will be appended to the shell-args specified for individual tests.
test_list: List of tests to be run in the format described in the
docstring of this module.
docstring of `mojo_test`.
Returns:
True iff all tests succeeded, False otherwise.
......
......@@ -6,10 +6,12 @@ import atexit
import datetime
import email.utils
import errno
import gzip
import hashlib
import logging
import math
import os.path
import shutil
import socket
import threading
......@@ -120,6 +122,7 @@ def _GetHandlerClassForPath(mappings):
path = self.translate_path(self.path)
if os.path.isfile(path):
self.send_header('Content-Encoding', 'gzip')
etag = self.get_etag()
if etag:
self.send_header('ETag', etag)
......@@ -136,21 +139,28 @@ def _GetHandlerClassForPath(mappings):
for prefix, local_base_path in mappings:
if normalized_path.startswith(prefix):
return os.path.join(local_base_path, normalized_path[len(prefix):])
result = os.path.join(local_base_path, normalized_path[len(prefix):])
if os.path.isfile(result):
gz_result = result + '.gz'
if (not os.path.isfile(gz_result) or
os.path.getmtime(gz_result) <= os.path.getmtime(result)):
with open(result, 'rb') as f:
with gzip.open(gz_result, 'wb') as zf:
shutil.copyfileobj(f, zf)
result = gz_result
return result
# This class is only used internally, and we're adding a catch-all ''
# prefix at the end of |mappings|.
assert False
def guess_type(self, path):
# This is needed so that Sky files without shebang can still run thanks to
# content-type mappings.
# This is needed so that exploded Sky apps without shebang can still run
# thanks to content-type mappings.
# TODO(ppi): drop this part once we can rely on the Sky files declaring
# correct shebang.
if path.endswith('.dart'):
if path.endswith('.dart') or path.endswith('.dart.gz'):
return 'application/dart'
elif path.endswith('.sky'):
return 'text/sky'
return SimpleHTTPServer.SimpleHTTPRequestHandler.guess_type(self, path)
def log_message(self, *_):
......
......@@ -9,9 +9,10 @@ checkouts.
"""
import os.path
import sys
def _lowest_ancestor_containing_relpath(relpath):
def find_ancestor_with(relpath):
"""Returns the lowest ancestor of this file that contains |relpath|."""
cur_dir_path = os.path.abspath(os.path.dirname(__file__))
while True:
......@@ -25,7 +26,7 @@ def _lowest_ancestor_containing_relpath(relpath):
return None
def infer_default_paths(is_android, is_debug, target_cpu):
def infer_mojo_paths(is_android, is_debug, target_cpu):
"""Infers the locations of select build output artifacts in a regular Mojo
checkout.
......@@ -38,7 +39,7 @@ def infer_default_paths(is_android, is_debug, target_cpu):
('Debug' if is_debug else 'Release'))
out_build_dir = os.path.join('out', build_dir)
root_path = _lowest_ancestor_containing_relpath(out_build_dir)
root_path = find_ancestor_with(out_build_dir)
if not root_path:
return None, ('Failed to find build directory: ' + out_build_dir)
......@@ -56,3 +57,33 @@ def infer_default_paths(is_android, is_debug, target_cpu):
paths['sky_packages'] = os.path.join(build_dir_path, 'gen', 'dart-pkg',
'packages')
return paths, None
# Based on Chromium //tools/find_depot_tools.py.
def find_depot_tools():
"""Searches for depot_tools.
Returns:
Path to the depot_tools checkout present on the machine, None if not found.
"""
def _is_real_depot_tools(path):
return os.path.isfile(os.path.join(path, 'gclient.py'))
# First look if depot_tools is already in PYTHONPATH.
for i in sys.path:
if i.rstrip(os.sep).endswith('depot_tools') and _is_real_depot_tools(i):
return i
# Then look if depot_tools is in PATH, common case.
for i in os.environ['PATH'].split(os.pathsep):
if _is_real_depot_tools(i):
return i
# Rare case, it's not even in PATH, look upward up to root.
root_dir = os.path.dirname(os.path.abspath(__file__))
previous_dir = os.path.abspath(__file__)
while root_dir and root_dir != previous_dir:
i = os.path.join(root_dir, 'depot_tools')
if _is_real_depot_tools(i):
return i
previous_dir = root_dir
root_dir = os.path.dirname(root_dir)
return None
......@@ -3,72 +3,99 @@
# found in the LICENSE file.
"""Functions that configure the shell before it is run manipulating its argument
list."""
list.
"""
import os.path
import urlparse
# When spinning up servers for local origins, we want to use predictable ports
# so that caching works between subsequent runs with the same command line.
_LOCAL_ORIGIN_PORT = 31840
_MAP_ORIGIN_BASE_PORT = 31841
_MAP_ORIGIN_PREFIX = '--map-origin='
_MAPPINGS_BASE_PORT = 31841
# Port on which the mojo:debugger http server will be available on the host
# machine.
_MOJO_DEBUGGER_PORT = 7777
_SKY_SERVER_PORT = 9998
def _HostLocalUrlDestination(shell, dest_file, port):
"""Starts a local server to host |dest_file|.
Returns:
Url of the hosted file.
"""
directory = os.path.dirname(dest_file)
if not os.path.exists(directory):
raise ValueError('local path passed as --map-url destination '
'does not exist')
server_url = shell.ServeLocalDirectory(directory, port)
return server_url + os.path.relpath(dest_file, directory)
def _IsMapOrigin(arg):
"""Returns whether |arg| is a --map-origin argument."""
return arg.startswith(_MAP_ORIGIN_PREFIX)
def _HostLocalOriginDestination(shell, dest_dir, port):
"""Starts a local server to host |dest_dir|.
def _Split(l, pred):
positive = []
negative = []
for v in l:
if pred(v):
positive.append(v)
else:
negative.append(v)
return (positive, negative)
Returns:
Url of the hosted directory.
"""
return shell.ServeLocalDirectory(dest_dir, port)
def _RewriteMapOriginParameter(shell, mapping, device_port):
parts = mapping[len(_MAP_ORIGIN_PREFIX):].split('=')
def _Rewrite(mapping, host_destination_functon, shell, port):
"""Takes a mapping given as <src>=<dest> and rewrites the <dest> part to be
hosted locally using the given function if <dest> is not a web url.
"""
parts = mapping.split('=')
if len(parts) != 2:
raise ValueError('each mapping value should be in format '
'"<url>=<url-or-local-path>"')
if urlparse.urlparse(parts[1])[0]:
# The destination is a web url, do nothing.
return mapping
dest = parts[1]
# If the destination is a url, don't map it.
if urlparse.urlparse(dest)[0]:
return mapping
# Assume the destination is a local directory and serve it.
localUrl = shell.ServeLocalDirectory(dest, device_port)
print 'started server at %s for %s' % (dest, localUrl)
return _MAP_ORIGIN_PREFIX + parts[0] + '=' + localUrl
src = parts[0]
dest = host_destination_functon(shell, parts[1], port)
return src + '=' + dest
def RewriteMapOriginParameters(shell, original_arguments):
"""Spawns a server for each local destination indicated in a map-origin
argument in |original_arguments| and rewrites it to point to the server url.
The arguments other than "map-origin" and "map-origin" arguments pointing to
web urls are left intact.
def ApplyMappings(shell, original_arguments, map_urls, map_origins):
"""Applies mappings for specified urls and origins. For each local path
specified as destination a local server will be spawned and the mapping will
be rewritten accordingly.
Args:
shell: The shell that is being configured.
original_arguments: List of arguments to be rewritten.
original_arguments: Current list of shell arguments.
map_urls: List of url mappings, each in the form of
<url>=<url-or-local-path>.
map_origins: List of origin mappings, each in the form of
<origin>=<url-or-local-path>.
Returns:
The updated argument list.
"""
map_arguments, other_arguments = _Split(original_arguments, _IsMapOrigin)
arguments = other_arguments
next_port = _MAP_ORIGIN_BASE_PORT
for mapping in sorted(map_arguments):
arguments.append(_RewriteMapOriginParameter(shell, mapping, next_port))
next_port += 1
return arguments
next_port = _MAPPINGS_BASE_PORT
args = original_arguments
if map_urls:
# Sort the mappings to preserve caching regardless of argument order.
for map_url in sorted(map_urls):
mapping = _Rewrite(map_url, _HostLocalUrlDestination, shell, next_port)
next_port += 1
# All url mappings need to be coalesced into one shell argument.
args = AppendToArgument(args, '--url-mappings=', mapping)
if map_origins:
for map_origin in sorted(map_origins):
mapping = _Rewrite(map_origin, _HostLocalOriginDestination, shell,
next_port)
next_port += 1
# Origin mappings are specified as separate, repeated shell arguments.
args.append('--map-origin=' + mapping)
return args
def ConfigureDebugger(shell):
......@@ -83,6 +110,47 @@ def ConfigureDebugger(shell):
return ['mojo:debugger %d' % _MOJO_DEBUGGER_PORT]
def ConfigureSky(shell, root_path, sky_packages_path, sky_target):
"""Configures additional mappings and a server needed to run the given Sky
app.
Args:
root_path: Local path to the root from which Sky apps will be served.
sky_packages_path: Local path to the root from which Sky packages will be
served.
sky_target: Path to the Sky app to be run, relative to |root_path|.
Returns:
Arguments that need to be appended to the shell argument list.
"""
# Configure a server to serve the checkout root at / (so that Sky examples
# are accessible using a root-relative path) and Sky packages at /packages.
# This is independent from the server that potentially serves the origin
# directory containing the mojo: apps.
additional_mappings = [
('packages/', sky_packages_path),
]
server_url = shell.ServeLocalDirectory(root_path, port=_SKY_SERVER_PORT,
additional_mappings=additional_mappings)
args = []
# Configure the content type mappings for the sky_viewer. This is needed
# only for the Sky apps that do not declare mojo:sky_viewer in a shebang,
# and it is unfortunate as it configures the shell to map all items of the
# application/dart content-type as Sky apps.
# TODO(ppi): drop this part once we can rely on the Sky files declaring
# correct shebang.
args = AppendToArgument(args, '--content-handlers=',
'text/sky,mojo:sky_viewer')
args = AppendToArgument(args, '--content-handlers=',
'application/dart,mojo:sky_viewer')
# Configure the window manager to embed the sky_viewer.
sky_url = server_url + sky_target
args.append('mojo:window_manager %s' % sky_url)
return args
def ConfigureLocalOrigin(shell, local_dir, fixed_port=True):
"""Sets up a local http server to serve files in |local_dir| along with
device port forwarding if needed.
......
......@@ -15,6 +15,7 @@ import tempfile
from android_gdb.install_remote_file_reader import install
from devtoolslib import paths
_MOJO_DEBUGGER_PORT = 7777
......@@ -29,40 +30,10 @@ class DirectoryNotFoundException(Exception):
def _get_dir_above(dirname):
"""Returns the directory "above" this file containing |dirname|."""
path = os.path.abspath(__file__)
while True:
path, tail = os.path.split(path)
if not tail:
raise DirectoryNotFoundException(dirname)
if dirname in os.listdir(path):
return path
# The two methods below are taken from //tools/find_depot_tools.py.
def _is_real_depot_tools(path):
return os.path.isfile(os.path.join(path, 'gclient.py'))
def _get_depot_tools_path():
"""Searches for depot_tools."""
# First look if depot_tools is already in PYTHONPATH.
for i in sys.path:
if i.rstrip(os.sep).endswith('depot_tools') and _is_real_depot_tools(i):
return i
# Then look if depot_tools is in PATH, common case.
for i in os.environ['PATH'].split(os.pathsep):
if _is_real_depot_tools(i):
return i
# Rare case, it's not even in PATH, look upward up to root.
root_dir = os.path.dirname(os.path.abspath(__file__))
previous_dir = os.path.abspath(__file__)
while root_dir and root_dir != previous_dir:
i = os.path.join(root_dir, 'depot_tools')
if _is_real_depot_tools(i):
return i
previous_dir = root_dir
root_dir = os.path.dirname(root_dir)
return None
path = paths.find_ancestor_with(dirname)
if not path:
raise DirectoryNotFoundException(dirname)
return path
def _send_request(request, payload=None):
......@@ -205,18 +176,33 @@ def _gdb_attach(args):
install_args['gsutil'] = os.path.join(args.gsutil_dir, 'gsutil')
else:
try:
install_args['gsutil'] = os.path.join(
_get_depot_tools_path(), 'third_party', 'gsutil',
'gsutil')
depot_tools_path = paths.find_depot_tools()
if not depot_tools_path:
raise DirectoryNotFoundException()
install_args['gsutil'] = os.path.join(depot_tools_path, 'third_party',
'gsutil', 'gsutil')
if not os.path.exists(install_args['gsutil']):
raise DirectoryNotFoundException()
except DirectoryNotFoundException:
logging.fatal("Unable to find gsutil, please specify its path with " "--gsutil-dir.")
logging.fatal("Unable to find gsutil, please specify its path with "
"--gsutil-dir.")
return
if args.adb_path:
install_args['adb'] = args.adb_path
install(**install_args)
else:
install_args['adb'] = 'adb'
try:
install(**install_args)
except OSError as e:
if e.errno == 2:
# ADB not found in path, print an error message
logging.fatal("Unable to find ADB, please specify its path with "
"--adb-path.")
return
else:
raise
gdb_path = os.path.join(
ndk_dir,
......@@ -248,11 +234,19 @@ def _gdb_attach(args):
debug_session_arguments["package_name"] = args.package_name
else:
debug_session_arguments["package_name"] = _DEFAULT_PACKAGE_NAME
debug_session_arguments['adb'] = install_args['adb']
if args.pyelftools_dir:
debug_session_arguments["pyelftools_dir"] = args.pyelftools_dir
else:
debug_session_arguments["pyelftools_dir"] = os.path.join(
_get_dir_above('third_party'), 'third_party', 'pyelftools')
try:
debug_session_arguments["pyelftools_dir"] = os.path.join(
_get_dir_above('third_party'), 'third_party', 'pyelftools')
if not os.path.exists(debug_session_arguments["pyelftools_dir"]):
raise DirectoryNotFoundException()
except DirectoryNotFoundException:
logging.fatal("Unable to find pyelftools python module, please specify "
"its path using --pyelftools-dir.")
return
debug_session_arguments_str = ', '.join(
[k + '="' + codecs.encode(v, 'string_escape') + '"'
......
......@@ -9,28 +9,26 @@ import sys
from devtoolslib.android_shell import AndroidShell
from devtoolslib.linux_shell import LinuxShell
from devtoolslib import paths
from devtoolslib import shell_arguments
from devtoolslib import default_paths
USAGE = ("mojo_shell "
USAGE = ("mojo_run "
"[--args-for=<mojo-app>] "
"[--content-handlers=<handlers>] "
"[--enable-external-applications] "
"[--disable-cache] "
"[--enable-multiprocess] "
"[--url-mappings=from1=to1,from2=to2] "
"[<mojo-app>] "
"""
A <mojo-app> is a Mojo URL or a Mojo URL and arguments within quotes.
Example: mojo_shell "mojo:js_standalone test.js".
Example: mojo_run "mojo:js_standalone test.js".
<url-lib-path> is searched for shared libraries named by mojo URLs.
The value of <handlers> is a comma separated list like:
text/html,mojo:html_viewer,application/javascript,mojo:js_content_handler
""")
_DEFAULT_WINDOW_MANAGER = "mojo:kiosk_wm"
_SKY_SERVER_PORT = 9998
def main():
......@@ -49,8 +47,20 @@ def main():
parser.add_argument('--target-cpu', help='CPU architecture to run for.',
choices=['x64', 'x86', 'arm'])
# Arguments indicating paths to binaries and tools.
parser.add_argument('--adb-path', help='Path of the adb binary.')
parser.add_argument('--shell-path', help='Path of the Mojo shell binary.')
parser.add_argument('--origin-path', help='Path of a directory to be set as '
'the origin for mojo: urls')
# Arguments configuring the shell run.
parser.add_argument('--origin', help='Origin for mojo: URLs.')
parser.add_argument('--map-url', action='append',
help='Define a mapping for a url in the format '
'<url>=<url-or-local-file-path>')
parser.add_argument('--map-origin', action='append',
help='Define a mapping for a url origin in the format '
'<origin>=<url-or-local-file-path>')
parser.add_argument('--window-manager', default=_DEFAULT_WINDOW_MANAGER,
help='Window manager app to be mapped as '
'mojo:window_manager. By default it is ' +
......@@ -75,30 +85,68 @@ def main():
'for off-screen rendering.')
launcher_args, args = parser.parse_known_args()
paths, error_msg = default_paths.infer_default_paths(launcher_args.android,
launcher_args.debug,
launcher_args.target_cpu)
if not paths:
print error_msg
return -1
# |mojo_paths| is a hack that allows to automagically get the correct
# configuration when running within the Mojo repository. This will go away
# once we have devtools config files, see
# https://github.com/domokit/devtools/issues/28.
mojo_paths, _ = paths.infer_mojo_paths(launcher_args.android,
launcher_args.debug,
launcher_args.target_cpu)
if mojo_paths:
if launcher_args.android:
adb_path = mojo_paths['adb']
shell_binary_path = mojo_paths['shell']
local_origin_path = mojo_paths['build']
if launcher_args.verbose:
print 'Running within a Chromium-style checkout.'
else:
if launcher_args.android:
adb_path = 'adb'
shell_binary_path = None
local_origin_path = '.'
if launcher_args.verbose:
print 'Running outside a Chromium-style checkout:'
else:
print 'Running outside a Mojo checkout is not supported on Linux yet.'
return 1
if launcher_args.adb_path:
adb_path = launcher_args.adb_path
if launcher_args.shell_path:
shell_binary_path = launcher_args.shell_path
if launcher_args.origin_path:
local_origin_path = launcher_args.origin_path
if launcher_args.verbose:
if shell_binary_path:
print 'Using the locally built shell at ' + shell_binary_path
if local_origin_path:
print 'Using the local origin of ' + local_origin_path
if launcher_args.android:
verbose_pipe = sys.stdout if launcher_args.verbose else None
shell = AndroidShell(paths['adb'], launcher_args.target_device,
shell = AndroidShell(adb_path, launcher_args.target_device,
logcat_tags=launcher_args.logcat_tags,
verbose_pipe=verbose_pipe)
device_status, error = shell.CheckDevice()
if not device_status:
print 'Device check failed: ' + error
return 1
shell.InstallApk(paths['shell'])
if shell_binary_path:
shell.InstallApk(shell_binary_path)
args = shell_arguments.RewriteMapOriginParameters(shell, args)
args = shell_arguments.ApplyMappings(shell, args, launcher_args.map_url,
launcher_args.map_origin)
if not launcher_args.origin:
args.extend(shell_arguments.ConfigureLocalOrigin(shell, paths['build']))
args.extend(shell_arguments.ConfigureLocalOrigin(shell,
local_origin_path))
else:
shell = LinuxShell(paths['shell'])
if not shell_binary_path:
print 'Can not run without a shell binary. Please pass --shell-path.'
return 1
shell = LinuxShell(shell_binary_path)
if launcher_args.use_osmesa:
args.append('--args-for=mojo:native_viewport_service --use-osmesa')
......@@ -111,30 +159,13 @@ def main():
args.extend(shell_arguments.ConfigureDebugger(shell))
if launcher_args.sky:
# Configure a server to serve the checkout root at / (so that Sky examples
# are accessible using a root-relative path) and Sky packages at /packages.
# This is independent from the server that potentially serves the origin
# directory containing the mojo: apps.
additional_mappings = [
('packages/', paths['sky_packages']),
]
server_url = shell.ServeLocalDirectory(paths['root'],
port=_SKY_SERVER_PORT, additional_mappings=additional_mappings)
# Configure the content type mappings for the sky_viewer. This is needed
# only for the Sky apps that do not declare mojo:sky_viewer in a shebang,
# and it is unfortunate as it configures the shell to map all items of the
# application/dart content-type as Sky apps.
# TODO(ppi): drop this part once we can rely on the Sky files declaring
# correct shebang.
args = shell_arguments.AppendToArgument(args, '--content-handlers=',
'text/sky,mojo:sky_viewer')
args = shell_arguments.AppendToArgument(args, '--content-handlers=',
'application/dart,mojo:sky_viewer')
# Configure the window manager to embed the sky_viewer.
sky_url = server_url + launcher_args.sky
args.append('mojo:window_manager %s' % sky_url)
if not mojo_paths:
print 'Running with --sky is not supported outside of the Mojo checkout.'
# See https://github.com/domokit/devtools/issues/27.
return 1
args.extend(shell_arguments.ConfigureSky(shell, mojo_paths['root'],
mojo_paths['sky_packages'],
launcher_args.sky))
if launcher_args.verbose:
print "Shell arguments: " + str(args)
......
#!/usr/bin/env python
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test runner for Mojo application tests.
The file describing the list of tests has to be a valid Python program that sets
a |tests| global variable, containing entries of the following form:
{
# Required URL for apptest.
"test": "mojo:test_app_url",
# Optional display name (otherwise the entry for "test" above is used).
"name": "mojo:test_app_url (more details)",
# Optional test type. Valid values:
# * "gtest" (default)
# * "gtest_isolated": like "gtest", but run with fixture isolation,
# i.e., each test in a fresh mojo_shell)
# * "dart".
"type": "gtest",
# Optional arguments for the apptest.
"test-args": ["--an_arg", "another_arg"],
# Optional arguments for the shell.
"shell-args": ["--some-flag-for-the-shell", "--another-flag"],
}
The program may use the |target_os| global that will be any of ['android',
'linux'], indicating the system on which the tests are to be run.
TODO(vtl|msw): Add a way of specifying data dependencies.
"""
import argparse
import sys
from devtoolslib.android_shell import AndroidShell
from devtoolslib.linux_shell import LinuxShell
from devtoolslib.apptest_runner import run_apptests
from devtoolslib import shell_arguments
def main():
parser = argparse.ArgumentParser(description="Test runner for Mojo "
"application tests.")
parser.add_argument("test_list_file", type=file,
help="a file listing apptests to run")
# Arguments indicating the configuration we are targeting.
parser.add_argument('--android', help='Run on Android',
action='store_true')
debug_group = parser.add_mutually_exclusive_group()
debug_group.add_argument('--debug', help='Debug build (default)',
default=True, action='store_true')
debug_group.add_argument('--release', help='Release build', default=False,
dest='debug', action='store_false')
parser.add_argument('--target-cpu', help='CPU architecture to run for.',
choices=['x64', 'x86', 'arm'])
# Arguments indicating paths to binaries and tools.
parser.add_argument('--adb-path', help='Path of the adb binary.')
parser.add_argument('--shell-path', help='Path of the Mojo shell binary.')
parser.add_argument('--origin-path', help='Path of a directory to be set as '
'the origin for mojo: urls')
args = parser.parse_args()
extra_shell_args = []
if args.android:
if not args.adb_path:
print 'Indicate path to adb in --adb-path.'
return 1
shell = AndroidShell(args.adb_path)
device_status, error = shell.CheckDevice()
if not device_status:
print 'Device check failed: ' + error
return 1
if not args.shell_path:
print 'Indicate path to the shell binary in --shell-path'
return 1
shell.InstallApk(args.shell_path)
if args.origin_path:
extra_shell_args.extend(shell_arguments.ConfigureLocalOrigin(
shell, args.origin_path, fixed_port=True))
else:
if not args.shell_path:
print 'Indicate path to the shell binary in --shell-path'
return 1
shell = LinuxShell(args.shell_path)
target_os = 'android' if args.android else 'linux'
test_list_globals = {"target_os": target_os}
exec args.test_list_file in test_list_globals
apptests_result = run_apptests(shell, extra_shell_args,
test_list_globals["tests"])
return 0 if apptests_result else 1
if __name__ == '__main__':
sys.exit(main())
......@@ -5,6 +5,8 @@
#ifndef MOJO_EDK_SYSTEM_CONNECTION_MANAGER_H_
#define MOJO_EDK_SYSTEM_CONNECTION_MANAGER_H_
#include <ostream>
#include "mojo/edk/system/connection_identifier.h"
#include "mojo/edk/system/process_identifier.h"
#include "mojo/edk/system/system_impl_export.h"
......@@ -60,6 +62,16 @@ namespace system {
// slave).
class MOJO_SYSTEM_IMPL_EXPORT ConnectionManager {
public:
enum class Result {
FAILURE = 0,
SUCCESS,
// These results are used for |Connect()| (which also uses |FAILURE|, but
// not |SUCCESS|).
SUCCESS_CONNECT_SAME_PROCESS,
SUCCESS_CONNECT_NEW_CONNECTION,
SUCCESS_CONNECT_REUSE_CONNECTION
};
virtual ~ConnectionManager() {}
ConnectionIdentifier GenerateConnectionIdentifier();
......@@ -86,14 +98,14 @@ class MOJO_SYSTEM_IMPL_EXPORT ConnectionManager {
virtual bool CancelConnect(const ConnectionIdentifier& connection_id) = 0;
// Connects a pending connection; to be called only after both parties have
// called |AllowConnect()|. On success, |peer_process_identifier| is set to an
// unique identifier for the peer process, and if the peer process is not the
// same as the calling process then |*platform_handle| is set to a suitable
// native handle connecting the two parties (if the two parties are the same
// process, then |*platform_handle| is reset to be invalid).
virtual bool Connect(const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle) = 0;
// called |AllowConnect()|. On success, |Result::SUCCESS_CONNECT_...| is
// returned and |peer_process_identifier| is set to an unique identifier for
// the peer process. In the case of |SUCCESS_CONNECT_SAME_PROCESS|,
// |*platform_handle| is set to a suitable native handle connecting the two
// parties.
virtual Result Connect(const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle) = 0;
protected:
// |platform_support| must be valid and remain alive until after |Shutdown()|
......@@ -107,6 +119,13 @@ class MOJO_SYSTEM_IMPL_EXPORT ConnectionManager {
MOJO_DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
};
// So logging macros and |DCHECK_EQ()|, etc. work.
MOJO_SYSTEM_IMPL_EXPORT inline std::ostream& operator<<(
std::ostream& out,
ConnectionManager::Result result) {
return out << static_cast<int>(result);
}
} // namespace system
} // namespace mojo
......
......@@ -141,8 +141,9 @@ embedder::ScopedPlatformHandle IPCSupport::ConnectToSlaveInternal(
system::ProcessIdentifier peer_id = system::kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle platform_connection_handle;
CHECK(connection_manager()->Connect(connection_id, &peer_id,
&platform_connection_handle));
CHECK_EQ(connection_manager()->Connect(connection_id, &peer_id,
&platform_connection_handle),
ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION);
DCHECK_EQ(peer_id, *slave_process_identifier);
DCHECK(platform_connection_handle.is_valid());
return platform_connection_handle;
......@@ -154,8 +155,9 @@ embedder::ScopedPlatformHandle IPCSupport::ConnectToMasterInternal(
system::ProcessIdentifier peer_id;
embedder::ScopedPlatformHandle platform_connection_handle;
CHECK(connection_manager()->Connect(connection_id, &peer_id,
&platform_connection_handle));
CHECK_EQ(connection_manager()->Connect(connection_id, &peer_id,
&platform_connection_handle),
ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION);
DCHECK_EQ(peer_id, system::kMasterProcessIdentifier);
DCHECK(platform_connection_handle.is_valid());
return platform_connection_handle;
......
......@@ -79,9 +79,9 @@ class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager final
void Shutdown() override MOJO_NOT_THREAD_SAFE;
bool AllowConnect(const ConnectionIdentifier& connection_id) override;
bool CancelConnect(const ConnectionIdentifier& connection_id) override;
bool Connect(const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle) override;
Result Connect(const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle) override;
private:
class Helper;
......@@ -92,10 +92,10 @@ class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager final
const ConnectionIdentifier& connection_id);
bool CancelConnectImpl(ProcessIdentifier process_identifier,
const ConnectionIdentifier& connection_id);
bool ConnectImpl(ProcessIdentifier process_identifier,
const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle);
Result ConnectImpl(ProcessIdentifier process_identifier,
const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle);
// These should only be called on |private_thread_|:
void ShutdownOnPrivateThread() MOJO_NOT_THREAD_SAFE;
......
......@@ -80,11 +80,18 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
CONNECTION_MANAGER_ALLOW_CONNECT = 0,
CONNECTION_MANAGER_CANCEL_CONNECT = 1,
CONNECTION_MANAGER_CONNECT = 2,
// Subtypes for type |Type::CONNECTION_MANAGER_ACK| (failure acks never have
// any message contents; success acks for "connect" always have a
// |ProcessIdentifier| as data and *may* have a platform handle attached):
// Subtypes for type |Type::CONNECTION_MANAGER_ACK|, corresponding to
// |ConnectionManager::Result| values (failure and non-"connect" acks never
// have any message contents; success acks for "connect" always have a
// |ProcessIdentifier| as data and also a platform handle attached for "new
// connection"):
// TODO(vtl): FIXME -- probably, in the "connect, reuse connection" case,
// we'll have to send more information.
CONNECTION_MANAGER_ACK_FAILURE = 0,
CONNECTION_MANAGER_ACK_SUCCESS = 1,
CONNECTION_MANAGER_ACK_SUCCESS_CONNECT_SAME_PROCESS = 2,
CONNECTION_MANAGER_ACK_SUCCESS_CONNECT_NEW_CONNECTION = 3,
CONNECTION_MANAGER_ACK_SUCCESS_CONNECT_REUSE_CONNECTION = 4,
};
// Messages (the header and data) must always be aligned to a multiple of this
......
......@@ -58,20 +58,20 @@ class MOJO_SYSTEM_IMPL_EXPORT SlaveConnectionManager final
void Shutdown() override;
bool AllowConnect(const ConnectionIdentifier& connection_id) override;
bool CancelConnect(const ConnectionIdentifier& connection_id) override;
bool Connect(const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle) override;
Result Connect(const ConnectionIdentifier& connection_id,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle) override;
private:
// These should only be called on |private_thread_|:
void InitOnPrivateThread(embedder::ScopedPlatformHandle platform_handle);
void ShutdownOnPrivateThread();
void AllowConnectOnPrivateThread(const ConnectionIdentifier& connection_id,
bool* result);
Result* result);
void CancelConnectOnPrivateThread(const ConnectionIdentifier& connection_id,
bool* result);
Result* result);
void ConnectOnPrivateThread(const ConnectionIdentifier& connection_id,
bool* result,
Result* result,
ProcessIdentifier* peer_process_identifier,
embedder::ScopedPlatformHandle* platform_handle);
......@@ -112,7 +112,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SlaveConnectionManager final
AWAITING_CONNECT_ACK
};
AwaitingAckType awaiting_ack_type_;
bool* ack_result_;
Result* ack_result_;
// Used only when waiting for the ack to "connect":
ProcessIdentifier* ack_peer_process_identifier_;
embedder::ScopedPlatformHandle* ack_platform_handle_;
......
......@@ -56,10 +56,6 @@ void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) {
return static_cast<GLES2Context*>(context)->interface();
}
void* MojoGLES2GetContextSupport(MojoGLES2Context context) {
return static_cast<GLES2Context*>(context)->context_support();
}
void MojoGLES2SignalSyncPoint(MojoGLES2Context context,
uint32_t sync_point,
MojoGLES2SignalSyncPointCallback callback,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
analyzer:
exclude:
- 'sdk_ext/**'
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册