[gnome-continuous] Add "applicationstest" task
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-continuous] Add "applicationstest" task
- Date: Mon, 30 Sep 2013 13:21:43 +0000 (UTC)
commit 28e32ef7a2d81605b8df111652f0d7e09c5d7561
Author: Colin Walters <walters verbum org>
Date: Fri Sep 20 07:44:41 2013 -0400
Add "applicationstest" task
This runs each app, checking to see if it core dumps, and takes a
screenshot.
As part of implementing this, we now support a much more generic
DBus-based command channel between guest and host.
Makefile-ostbuild.am | 1 +
Makefile-tests.am | 1 +
extras/build.gnome.org/build.gnome.org.js | 2 +-
extras/build.gnome.org/index.html | 1 +
src/js/tasks/task-applicationstest.js | 119 ++++++++++++++++++
src/js/tasks/testbase.js | 152 +++++++++++++++++++++---
src/tests/gnome-continuous-startstopapps | 186 +++++++++++++++++++++++++++++
7 files changed, 445 insertions(+), 17 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index ae6552c..392ed8b 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -97,6 +97,7 @@ jsosttasks_DATA= \
src/js/tasks/task-builddisks.js \
src/js/tasks/task-smoketest.js \
src/js/tasks/task-integrationtest.js \
+ src/js/tasks/task-applicationstest.js \
src/js/tasks/task-zdisks.js \
src/js/tasks/testbase.js \
$(NULL)
diff --git a/Makefile-tests.am b/Makefile-tests.am
index 7c89d92..f9233ae 100644
--- a/Makefile-tests.am
+++ b/Makefile-tests.am
@@ -19,5 +19,6 @@ if BUILDSYSTEM
testdatadir = $(pkgdatadir)/tests
testdata_DATA = src/tests/gnome-ostree-export-journal-to-serialdev \
src/tests/gnome-ostree-export-journal-to-serialdev.service \
+ src/tests/gnome-continuous-startstopapps \
$(NULL)
endif
diff --git a/extras/build.gnome.org/build.gnome.org.js b/extras/build.gnome.org/build.gnome.org.js
index b85aad5..3d4a714 100644
--- a/extras/build.gnome.org/build.gnome.org.js
+++ b/extras/build.gnome.org/build.gnome.org.js
@@ -9,7 +9,7 @@
var repoDataSignal = {};
var taskData = {};
- var taskNames = ['build', 'smoketest', 'integrationtest'];
+ var taskNames = ['build', 'smoketest', 'integrationtest', 'applicationstest'];
function _loadTask(taskname) {
var url = WORKURL + 'tasks/' + taskname + '/current/meta.json';
diff --git a/extras/build.gnome.org/index.html b/extras/build.gnome.org/index.html
index dd252c9..b1d0a46 100644
--- a/extras/build.gnome.org/index.html
+++ b/extras/build.gnome.org/index.html
@@ -62,6 +62,7 @@
<p>Build: <a href="" id="build-link"></a><span id="build-span"></span></p>
<p>Smoketest: <a href="" id="smoketest-link"></a><span id="smoketest-span"></span></p>
<p>Integrationtest: <a href="" id="integrationtest-link"></a><span
id="integrationtest-span"></span></p>
+ <p>Applicationstest: <a href="" id="applicationstest-link"></a><span
id="applicationstest-span"></span></p>
<p>Maintainer: Colin Walters <walters verbum org></p>
<p>
<center>
diff --git a/src/js/tasks/task-applicationstest.js b/src/js/tasks/task-applicationstest.js
new file mode 100644
index 0000000..f992b64
--- /dev/null
+++ b/src/js/tasks/task-applicationstest.js
@@ -0,0 +1,119 @@
+// -*- indent-tabs-mode: nil; tab-width: 2; -*-
+// Copyright (C) 2013 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+const GLib = imports.gi.GLib;
+const OSTree = imports.gi.OSTree;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Builtin = imports.builtin;
+const ArgParse = imports.argparse;
+const ProcUtil = imports.procutil;
+const Task = imports.task;
+const TestBase = imports.tasks.testbase;
+const LibQA = imports.libqa;
+const JSUtil = imports.jsutil;
+const JSONUtil = imports.jsonutil;
+
+// From ot-gio-utils.h.
+// XXX: Introspect this.
+const OSTREE_GIO_FAST_QUERYINFO =
("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," +
+ "unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev");
+
+const TaskApplicationsTest = new Lang.Class({
+ Name: 'TaskApplicationsTest',
+ Extends: TestBase.TestBase,
+
+ TaskDef: {
+ TaskName: "applicationstest",
+ TaskAfter: ['smoketest'],
+ },
+
+ RequiredMessageIDs: ["6912513dead443cea8ddb6b716185fa5" // Application test complete
+ ],
+
+ FailedMessageIDs: [],
+
+ CompletedTag: 'applicationstest',
+
+ _handleMessage: function(message, cancellable) {
+ // coredump
+ if (message['MESSAGE_ID'] == "fc2e22bc6ee647b6b90729ab34a250b1") {
+ print(message['MESSAGE']);
+ if (this._testingApp != null) {
+ this._testingAppCoredumped = true;
+ }
+ }
+ },
+
+ _onCommandChannelAsyncMessage: function(msgId, value) {
+ if (msgId == 'TestingAppStart') {
+ this._testingApp = value.deep_unpack();
+ print("got testingAppStart id=" + this._testingApp);
+ this._allApps[this._testingApp] = {'state': 'running'};
+ GSystem.file_ensure_directory(this._statusPath.get_parent(), true, null);
+ this._testingAppCoredumped = false;
+ } else if (msgId == 'TestingAppTimedOut') {
+ print("got TestingAppTimedOut");
+ this._allApps[this._testingApp] = {'state': 'timeout'};
+ this._testingApp = null;
+ } else if (msgId == 'TestingAppComplete') {
+ let successfulStr = !this._testingAppCoredumped ? 'success' : 'failed';
+ print("got TestingAppComplete success=" + successfulStr);
+ this._allApps[this._testingApp] = {'state': successfulStr};
+ this._testingApp = null;
+ } else {
+ print("Got unknown asyncmessage: " + msgId);
+ }
+ },
+
+ _onSuccess: function() {
+ print("Successful; allApps=" + JSON.stringify(this._allApps));
+ let appsDataPath = Gio.File.new_for_path('apps.json');
+ JSONUtil.writeJsonFileAtomic(appsDataPath, {'apps': this._allApps }, null);
+ },
+
+ _prepareDisk: function(mntdir, arch, cancellable) {
+ this._allApps = {};
+ let osname = this._buildData['snapshot']['osname'];
+ let datadir = LibQA.getDatadir();
+ let startStopAppsName = 'gnome-continuous-startstopapps';
+ let startStopAppsSrc = datadir.resolve_relative_path('tests/' + startStopAppsName);
+ let [deployDir, deployEtcDir] = LibQA.getDeployDirs(mntdir, osname);
+ let startStopAppsDest = deployDir.resolve_relative_path('usr/bin/' + startStopAppsName);
+ print("Copying to " + startStopAppsDest.get_path());
+ startStopAppsSrc.copy(startStopAppsDest, Gio.FileCopyFlags.OVERWRITE, cancellable, null, null);
+ GSystem.file_chmod(startStopAppsDest, 493, cancellable);
+ let desktopFile = '[Desktop Entry]\n\
+Encoding=UTF-8\n\
+Name=GNOME Applications\n\
+Exec=/usr/bin/gnome-continuous-startstopapps\n\
+Terminal=false\n\
+Type=Application\n';
+ let dest =
deployEtcDir.resolve_relative_path('xdg/autostart/gnome-continuous-startstop-apps.desktop');
+ GSystem.file_ensure_directory(dest.get_parent(), true, cancellable);
+ dest.replace_contents(desktopFile, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION,
+ cancellable);
+ let commandChannelUdevRule = 'ACTION=="add", SUBSYSTEM=="virtio-ports", MODE="0666"'
+ let udevRuleDest =
deployDir.resolve_relative_path('usr/lib/udev/rules.d/42-gnome-continuous-world-rw-virtio.rules');
+ udevRuleDest.replace_contents(commandChannelUdevRule, null, false,
Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
+ }
+});
diff --git a/src/js/tasks/testbase.js b/src/js/tasks/testbase.js
index 8fba627..c01a139 100644
--- a/src/js/tasks/testbase.js
+++ b/src/js/tasks/testbase.js
@@ -26,6 +26,7 @@ const GSystem = imports.gi.GSystem;
const Builtin = imports.builtin;
const ArgParse = imports.argparse;
const ProcUtil = imports.procutil;
+const Params = imports.params;
const Task = imports.task;
const LibQA = imports.libqa;
const JSUtil = imports.jsutil;
@@ -34,6 +35,44 @@ const JSONUtil = imports.jsonutil;
const TIMEOUT_SECONDS = 10 * 60;
const COMPLETE_IDLE_WAIT_SECONDS = 10;
+const CommandSocketIface = <interface name="org.gnome.Continuous.Command">
+ <method name='AsyncMessage'>
+ <arg name="msgId" direction="in" type="s"/>
+ <arg name="value" direction="in" type="v"/>
+ </method>
+ <method name='Screenshot'>
+ <arg name="name" direction="in" type="s"/>
+ </method>
+ <signal name='ScreenshotComplete'>
+ <arg name="name" direction="out" type="s"/>
+ </signal>
+</interface>;
+
+// This proxy class exists to avoid tangling up the TestOneDisk class
+// with the Command DBus API. It also keeps track of the SaveFile API
+// state.
+const CommandSocketProxy = new Lang.Class({
+ Name: 'CommandSocketProxy',
+
+ _init: function(connection,
+ asyncMessageHandler,
+ screenshotHandler) {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(CommandSocketIface, this);
+ this._dbusImpl.export(connection, '/org/gnome/Continuous/Command');
+ this._asyncMessageHandler = asyncMessageHandler;
+ this._screenshotHandler = screenshotHandler;
+ this._savingFiles = {};
+ },
+
+ Screenshot: function(name) {
+ this._screenshotHandler(name);
+ },
+
+ AsyncMessage: function(msgId, value) {
+ this._asyncMessageHandler(msgId, value);
+ }
+});
+
const TestOneDisk = new Lang.Class({
Name: 'TestOneDisk',
@@ -49,7 +88,7 @@ const TestOneDisk = new Lang.Class({
return;
this._failed = true;
this._failedMessage = message;
- this._screenshot(true);
+ this._screenshot({ isFinal: true });
},
_onQemuExited: function(proc, result) {
@@ -122,6 +161,7 @@ const TestOneDisk = new Lang.Class({
if (this._countPendingRequiredMessageIds == 0 && !this._foundAllMessageIds) {
print("Found all required message IDs");
this._foundAllMessageIds = true;
+ this._parentTask._onSuccess();
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, COMPLETE_IDLE_WAIT_SECONDS,
Lang.bind(this, this._onFinalWait));
} else {
@@ -201,12 +241,12 @@ const TestOneDisk = new Lang.Class({
Lang.bind(this, this._onQemuCommandComplete));
},
- _onScreenshotComplete: function(filename, isFinal) {
+ _onScreenshotComplete: function(filename, isFinal, name) {
print("screenshot complete for " + filename);
let filePath = this._subworkdir.get_child(filename);
let modified = true;
- if (!isFinal) {
+ if (!isFinal && name == null) {
let contentsBytes = GSystem.file_map_readonly(filePath, this._cancellable);
let csum = GLib.compute_checksum_for_bytes(GLib.ChecksumType.SHA256,
contentsBytes);
@@ -238,28 +278,46 @@ const TestOneDisk = new Lang.Class({
}
GSystem.file_unlink(filePath, this._cancellable);
}
- this._requestingScreenshot = false;
- if (isFinal)
+ if (name == null) {
+ this._requestingScreenshot = false;
+ } else {
+ this._commandProxy._dbusImpl.emit_signal('ScreenshotComplete',
+ GLib.Variant.new('(s)', [name]));
+ }
+
+ if (isFinal) {
+ print("Final screenshot complete");
this._loop.quit();
+ }
},
- _screenshot: function(isFinal) {
- if (this._requestingScreenshot && !isFinal)
- return;
- this._requestingScreenshot = true;
+ _screenshot: function(params) {
+ params = Params.parse(params, { isFinal: false,
+ name: null });
+ if (params.name == null) {
+ if (this._requestingScreenshot)
+ return;
+ this._requestingScreenshot = true;
+ }
let filename;
- if (isFinal)
- filename = "screenshot-final.ppm";
- else
- filename = "screenshot-" + this._screenshotSerial + ".ppm";
+ if (params.name == null) {
+ if (params.isFinal)
+ filename = "screenshot-final.ppm";
+ else
+ filename = "screenshot-" + this._screenshotSerial + ".ppm";
+ } else {
+ filename = "screenshot-" + params.name + ".ppm";
+ }
print("requesting screenshot " + filename);
this._qmpCommand({"execute": "screendump", "arguments": { "filename": filename }},
- Lang.bind(this, this._onScreenshotComplete, filename, isFinal));
+ Lang.bind(this, this._onScreenshotComplete, filename, params.isFinal,
params.name));
},
_idleScreenshot: function() {
+ if (this._foundAllMessageIds)
+ return false;
print("idleScreenshot caps=" + this._qmpCapabilitiesReceived);
if (this._qmpCapabilitiesReceived)
this._screenshot(false);
@@ -269,7 +327,50 @@ const TestOneDisk = new Lang.Class({
_onFinalWait: function() {
print("Final wait complete");
- this._screenshot(true);
+ this._screenshot({ isFinal: true });
+ },
+
+ _onCommandChannelScreenshot: function(name) {
+ this._screenshot({ name: name });
+ },
+
+ _onCommandSocketDBusReady: function(iostream, result) {
+ this._commandSocketDBus = Gio.DBusConnection.new_finish(result);
+ this._commandProxy = new CommandSocketProxy(this._commandSocketDBus,
+ Lang.bind(this._parentTask,
this._parentTask._onCommandChannelAsyncMessage),
+ Lang.bind(this, this._onCommandChannelScreenshot));
+ print("Command DBus connection open");
+ },
+
+ _onCommandSocketConnected: function(client, result) {
+ this._commandSocketConn = client.connect_finish(result);
+ print("Connected to command socket");
+ Gio.DBusConnection.new(this._commandSocketConn, null, 0,
+ null, this._cancellable,
+ Lang.bind(this, this._onCommandSocketDBusReady));
+ },
+
+ _tryCommandConnection: function() {
+ if (this._commandSocketDBus || this._complete)
+ return false;
+ printerr("DEBUG: Querying command socket");
+ if (!this._commandSocketPath.query_exists(null)) {
+ print("commandSocketPath " + this._commandSocketPath.get_path() + " does not exist yet");
+ this._commandConnectionAttempts++;
+ if (this._commandConnectionAttempts > 5) {
+ this._fail("Command connection didn't appear at " + this._commandSocketPath.get_path());
+ this._loop.quit();
+ return false;
+ }
+ return true;
+ }
+ printerr("Connecting to command socket...");
+ let path = Gio.File.new_for_path('.').get_relative_path(this._commandSocketPath);
+ let address = Gio.UnixSocketAddress.new_with_type(path, Gio.UnixSocketAddressType.PATH);
+ let socketClient = new Gio.SocketClient();
+ socketClient.connect_async(address, this._cancellable,
+ Lang.bind(this, this._onCommandSocketConnected));
+ return false;
},
execute: function(subworkdir, buildData, repo, diskPath, cancellable) {
@@ -279,9 +380,12 @@ const TestOneDisk = new Lang.Class({
this._subworkdir = subworkdir;
this._loop = GLib.MainLoop.new(null, true);
this._foundAllMessageIds = false;
+ this._complete = false;
this._failed = false;
this._journalStream = null;
this._journalDataStream = null;
+ this._commandConnectionAttempts = 0;
+ this._commandSocketDBus = null;
this._openedJournal = false;
this._readingJournal = false;
this._pendingRequiredMessageIds = {};
@@ -336,7 +440,9 @@ const TestOneDisk = new Lang.Class({
let consoleOutput = subworkdir.get_child('console.out');
let journalOutput = subworkdir.get_child('journal-json.txt');
+ this._commandSocketPath = subworkdir.get_child('command.sock');
+ let commandSocketRelpath = subworkdir.get_relative_path(this._commandSocketPath);
qemuArgs.push.apply(qemuArgs, ['-drive', 'file=' + diskClone.get_path() + ',if=virtio',
'-vnc', 'none',
'-serial', 'file:' + consoleOutput.get_path(),
@@ -344,7 +450,9 @@ const TestOneDisk = new Lang.Class({
'-mon', 'chardev=charmonitor,id=monitor,mode=control',
'-device', 'virtio-serial',
'-chardev', 'file,id=journaljson,path=' + journalOutput.get_path(),
- '-device',
'virtserialport,chardev=journaljson,name=org.gnome.journaljson']);
+ '-device',
'virtserialport,chardev=journaljson,name=org.gnome.journaljson',
+ '-chardev', 'socket,id=commandchan,server,path=' +
commandSocketRelpath,
+ '-device',
'virtserialport,chardev=commandchan,name=org.gnome.commandchan']);
let qemuContext = new GSystem.SubprocessContext({ argv: qemuArgs });
qemuContext.set_cwd(subworkdir.get_path());
@@ -360,6 +468,8 @@ const TestOneDisk = new Lang.Class({
let journalMonitor = journalOutput.monitor_file(0, cancellable);
journalMonitor.connect('changed', Lang.bind(this, this._onJournalChanged));
+ let commandConnectAttemptTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1,
+ Lang.bind(this,
this._tryCommandConnection));
let timeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, TIMEOUT_SECONDS,
Lang.bind(this, this._onTimeout));
@@ -369,6 +479,8 @@ const TestOneDisk = new Lang.Class({
this._loop.run();
+ this._complete = true;
+
if (this._qemu)
this._qemu.force_exit();
@@ -410,10 +522,18 @@ const TestBase = new Lang.Class({
// Nothing, intended for subclasses
},
+ // For subclasses
+ _onCommandChannelAsyncMessage: function(msgId, value) {
+ print("Received command async message " + msgId);
+ },
+
_handleMessage: function(message, cancellable) {
return false;
},
+ _onSuccess: function() {
+ },
+
_postQemu: function(cancellable) {
},
diff --git a/src/tests/gnome-continuous-startstopapps b/src/tests/gnome-continuous-startstopapps
new file mode 100644
index 0000000..8c00a37
--- /dev/null
+++ b/src/tests/gnome-continuous-startstopapps
@@ -0,0 +1,186 @@
+#!/usr/bin/env gjs
+// -*- indent-tabs-mode: nil; tab-width: 2; -*-
+// Copyright (C) 2013 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+
+const sessionBus = Gio.bus_get_sync(Gio.BusType.SESSION, null);
+
+const CLOSE_ALL_APPS = 'Shell.AppSystem.get_default().get_running().forEach(function (app) {
app.request_quit(); });';
+const GET_APP_IDS = 'Shell.AppSystem.get_default().get_running().map(function (a) { return a.get_id(); });';
+
+const StartStopApps = new GObject.Class({
+ Name: 'StartStopApps',
+
+ _init: function(props) {
+ this._commandConnection = null;
+ },
+
+ _onShellAppeared: function() {
+ print("Shell appeared!");
+ // FIXME...looks like shell grabs the name but doesn't have
+ // objects exported yet
+ GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, Lang.bind(this, this._startTesting));
+ },
+
+ _startTesting: function() {
+ this._shell = Gio.DBusProxy.new_sync(sessionBus, 0, null,
+ "org.gnome.Shell", "/org/gnome/Shell",
+ "org.gnome.Shell", null);
+ this._appList = Gio.AppInfo.get_all().filter(function (app) {
+ if (app.get_nodisplay())
+ return false;
+ // Ok, a gross hack; we should really be using gnome-menus
+ // to look up all apps. Or maybe fix Gio?
+ if (app.has_key('Categories') &&
+ app.get_string('Categories').indexOf('X-GNOME-Settings-Panel') >= 0)
+ return false;
+ return true;
+ });
+ print("" + this._appList.length + " apps to test");
+ this._testingApp = null;
+
+ this._testNextApp();
+ },
+
+ _testNextApp: function() {
+ if (this._appList.length == 0) {
+ print("No applications remaining to test");
+ this._running = false;
+ return;
+ }
+
+ this._testingApp = this._appList.shift();
+ this._awaitingScreenshot = false;
+ let appid = this._testingApp.get_id();
+ print("testing appid=" + appid);
+
+ this._shellEval(CLOSE_ALL_APPS, this._cancellable);
+
+ this._sendAsyncMessage('TestingAppStart', GLib.Variant.new("s", appid));
+ this._testingApp.launch([], this._cancellable);
+ this._appCheckRunningIterations = 0;
+ this._appCheckRunningTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1,
+ Lang.bind(this,
this._checkAppRunningTimeout));
+ },
+
+ _onScreenshotComplete: function(name) {
+ print("Got ScreenshotComplete " + name);
+ let appid = this._testingApp.get_id();
+ this._sendAsyncMessage('TestingAppComplete', GLib.Variant.new("s", appid));
+ this._testNextApp();
+ },
+
+ _checkAppRunningTimeout: function() {
+ let appid = this._testingApp.get_id();
+ if (this._appCheckRunningIterations > 10) {
+ print("Timed out waiting for app " + appid);
+ this._sendAsyncMessage('TestingAppTimedOut', GLib.Variant.new("s", appid));
+ this._testNextApp();
+ return false;
+ }
+
+ let runningApps = this._shellEval(GET_APP_IDS, this._cancellable);
+ for (let i = 0; i < runningApps.length; i++) {
+ if (runningApps[i] != appid) {
+ print("WARNING: Unexpected application running: " + runningApps[i]);
+ continue;
+ }
+ this._requestScreenshot(appid);
+ this._awaitingScreenshot = true;
+ return false;
+ }
+
+ return true;
+ },
+
+ _onShellVanished: function() {
+ print("Shell vanished");
+ },
+
+ _shellEval: function(code, cancellable) {
+ let res = this._shell.call_sync("Eval", GLib.Variant.new("(s)", [code]), 0, -1,
+ cancellable).deep_unpack();
+ let [success, result] = res;
+ if (!success)
+ throw new Error("Failed to eval " + code.substr(0, 20));
+ return result ? JSON.parse(result) : null;
+ },
+
+ _ensureCommandConnection: function() {
+ if (this._commandConnection)
+ return;
+ let commandSocketFile = Gio.File.new_for_path('/dev/virtio-ports/org.gnome.commandchan');
+ let commandSocketIOStream = commandSocketFile.open_readwrite(null);
+ this._commandConnection = Gio.DBusConnection.new_sync(commandSocketIOStream, null, 0, null, null);
+ this._commandConnection.signal_subscribe(null, 'org.gnome.Continuous.Command',
+ 'ScreenshotComplete', '/org/gnome/Continuous/Command',
+ null, 0, Lang.bind(this, this._onScreenshotComplete));
+ },
+
+ _requestScreenshot: function(name) {
+ this._ensureCommandConnection();
+ this._commandConnection.call_sync(null, '/org/gnome/Continuous/Command',
+ 'org.gnome.Continuous.Command', 'Screenshot',
+ new GLib.Variant("(s)", [name, {}]),
+ null, 0, -1, null);
+ },
+
+ _sendAsyncMessage: function(msgId, content) {
+ this._ensureCommandConnection();
+ let msg = Gio.DBusMessage.new_method_call('org.gnome.Continuous.Command',
+ '/org/gnome/Continuous/Command',
+ 'org.gnome.Continuous.Command',
+ 'AsyncMessage');
+ msg.set_flags(Gio.DBusMessageFlags.NO_REPLY_EXPECTED);
+ if (content == null)
+ content = GLib.Variant.new("s", "");
+ msg.set_body(GLib.Variant.new("(sv)", [msgId, content]));
+ this._commandConnection.send_message(msg, 0);
+ },
+
+ run: function(cancellable) {
+ print("Awaiting org.gnome.Shell...");
+ this._running = true;
+ this._cancellable = cancellable;
+ sessionBus.watch_name('org.gnome.Shell', 0,
+ Lang.bind(this, this._onShellAppeared),
+ Lang.bind(this, this._onShellVanished));
+ let context = GLib.MainContext.default();
+ while (this._running)
+ context.iteration(true);
+ }
+});
+
+function main() {
+ let cancellable = null;
+
+ let startstopapps = new StartStopApps();
+ startstopapps.run(cancellable);
+ let [success, stdout, stderr, estatus] =
+ GLib.spawn_sync(null,
+ ['gnome-continuous-journal-log-hack',
+ '6912513dead443cea8ddb6b716185fa5=startstopapps complete'],
+ null, GLib.SpawnFlags.SEARCH_PATH, null);
+}
+
+printerr("Running gnome-continuous-startstopapps");
+main();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]