[gnome-continuous/wip/applications-testing: 1/2] wip



commit a00595763053c7a77af684e99734d769cf883ef3
Author: Colin Walters <walters verbum org>
Date:   Fri Sep 20 07:44:41 2013 -0400

    wip

 Makefile-ostbuild.am                     |    1 +
 Makefile-tests.am                        |    1 +
 src/js/tasks/task-applicationstest.js    |  119 +++++++++++++++++++
 src/js/tasks/testbase.js                 |  147 +++++++++++++++++++++---
 src/tests/gnome-continuous-startstopapps |  184 ++++++++++++++++++++++++++++++
 5 files changed, 437 insertions(+), 15 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/src/js/tasks/task-applicationstest.js b/src/js/tasks/task-applicationstest.js
new file mode 100644
index 0000000..5c470a9
--- /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.unpack();
+            this._allApps.push(this._testingApp);
+            this._statusPath = this._workdir.resolve_relative_path('apps/' + 
this._testingApp).get_child('result.json');
+            GSystem.file_ensure_directory(this._statusPath.get_parent(), true, null);
+            print("got TestingAppStart id=" + this._testingApp);
+            this._testingAppCoredumped = false;
+        } else if (msgId == 'TestingAppTimedOut') {
+            print("got TestingAppTimedOut");
+            JSONUtil.writeJsonFileAtomic(this._statusPath, {'status': 'failed',
+                                                            'reason': 'timeout'});
+            this._testingApp = null;
+        } else if (msgId == 'TestingAppComplete') {
+            let successfulStr = !this._testingAppCoredumped ? 'success' : 'failed';
+            print("got TestingAppComplete success=" + successfulStr);
+            JSONUtil.writeJsonFileAtomic(this._statusPath, {'status': successfulStr}, null);
+            this._testingApp = null;
+        } else {
+            print("Got unknown asyncmessage: " + msgId);
+        }
+    },
+
+    _onSuccess: function() {
+        print("Successful");
+    },
+   
+    _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..85232bb 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,45 @@ 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,
+                    saveFileHandler) {
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(CommandSocketIface, this);
+        this._dbusImpl.export(connection, '/org/gnome/Continuous/Command');
+        this._asyncMessageHandler = asyncMessageHandler;
+        this._saveFileHandler = saveFileHandler;
+        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 +89,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 +162,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 +242,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,25 +279,39 @@ const TestOneDisk = new Lang.Class({
             }
             GSystem.file_unlink(filePath, this._cancellable);
         }
-        this._requestingScreenshot = false;
+
+        if (name == null) {
+            this._requestingScreenshot = false;
+        } else {
+            this._commandProxy.emit_signal('ScreenshotComplete',
+                                           GLib.Variant.new('(s)', [name]));
+        }
 
         if (isFinal)
             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() {
@@ -269,7 +324,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, 
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 +377,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 +437,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 +447,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 +465,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 +476,8 @@ const TestOneDisk = new Lang.Class({
         
         this._loop.run();
 
+        this._complete = true;
+
         if (this._qemu)
             this._qemu.force_exit();
 
@@ -410,10 +519,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..8460624
--- /dev/null
+++ b/src/tests/gnome-continuous-startstopapps
@@ -0,0 +1,184 @@
+#!/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;
+       });
+       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([], 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;
+       }
+
+       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]