[gnome-ostree] Support installed tests, add integrationtest task



commit c81e11a94af247268c1bfa0096a6c3f23f649b1e
Author: Colin Walters <walters verbum org>
Date:   Fri Apr 19 02:52:27 2013 -0400

    Support installed tests, add integrationtest task
    
    This allows us to fully automate running test suites.

 Makefile-ostbuild.am                 |    1 +
 manifest.json                        |    7 +-
 src/js/tasks/task-build.js           |  242 +++++++++++++++++++++++-----------
 src/js/tasks/task-integrationtest.js |   69 ++++++++++
 src/js/tasks/task-smoketest.js       |    6 +-
 src/js/tasks/testbase.js             |   43 +++++-
 6 files changed, 282 insertions(+), 86 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index a0bcc08..c6810c1 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -75,6 +75,7 @@ jsosttasks_DATA= \
        src/js/tasks/task-bdiff.js \
        src/js/tasks/task-builddisks.js \
        src/js/tasks/task-smoketest.js \
+       src/js/tasks/task-integrationtest.js \
        src/js/tasks/task-zdisks.js \
        src/js/tasks/testbase.js \
        $(NULL)
diff --git a/manifest.json b/manifest.json
index ba8c55b..2aacfaf 100644
--- a/manifest.json
+++ b/manifest.json
@@ -849,9 +849,14 @@
 
                {"src": "gnome:nautilus"},
 
-               {"src": "gnome:gnome-software"}
+               {"src": "gnome:gnome-software"},
+
+               {"src": "gnome:gnome-desktop-testing",
+                "component": "testing"}
        ],
 
+    "installed-tests-components": ["gjs"],
+
     "initramfs-build-epoch": {"version": 0},
     
     "build-epoch": {"version": 8,
diff --git a/src/js/tasks/task-build.js b/src/js/tasks/task-build.js
index a771698..01ce841 100644
--- a/src/js/tasks/task-build.js
+++ b/src/js/tasks/task-build.js
@@ -24,6 +24,7 @@ const GSystem = imports.gi.GSystem;
 
 const Builtin = imports.builtin;
 const Task = imports.task;
+const Params = imports.params;
 const JsonDB = imports.jsondb;
 const FileUtil = imports.fileutil;
 const ProcUtil = imports.procutil;
@@ -72,46 +73,20 @@ const TaskBuild = new Lang.Class({
        }
     },
 
-    _composeBuildroot: function(workdir, componentName, architecture, cancellable) {
+    _composeBuildrootCore: function(workdir, componentName, architecture, rootContents, cancellable) {
         let starttime = GLib.DateTime.new_now_utc();
 
         let buildname = Format.vprintf('%s/%s/%s', [this.osname, componentName, architecture]);
         let buildrootCachedir = this.cachedir.resolve_relative_path('roots/' + buildname);
         GSystem.file_ensure_directory(buildrootCachedir, true, cancellable);
 
-        let components = this._snapshot.data['components']
-        let component = null;
-        let buildDependencies = [];
-        for (let i = 0; i < components.length; i++) {
-           let component = components[i];
-            if (component['name'] == componentName)
-                break;
-            buildDependencies.push(component);
-       }
-
-        let refToRev = {};
-
-        let archBuildrootName = Format.vprintf('%s/bases/%s/%s-devel', [this.osname,
-                                                                          
this._snapshot.data['base']['name'],
-                                                                          architecture]);
-
-        print("Computing buildroot contents");
-
-        let archBuildrootRev = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + 
this.repo.get_path(), 'rev-parse',
-                                                                     archBuildrootName], cancellable);
-
-        refToRev[archBuildrootName] = archBuildrootRev;
-        let checkoutTrees = [[archBuildrootName, '/']];
-        let refsToResolve = [];
-        for (let i = 0; i < buildDependencies.length; i++) {
-           let dependency = buildDependencies[i];
-            let buildname = Format.vprintf('%s/components/%s/%s', [this.osname, dependency['name'], 
architecture]);
-            refsToResolve.push(buildname);
-            checkoutTrees.push([buildname, '/runtime']);
-            checkoutTrees.push([buildname, '/devel']);
+       let refsToResolve = []
+       for (let i = 0; i < rootContents.length; i++) {
+           refsToResolve.push(rootContents[i][0]);
        }
 
         let resolvedRefs = this._resolveRefs(refsToResolve);
+        let refToRev = {};
        for (let i = 0; i < refsToResolve.length; i++) {
            refToRev[refsToResolve[i]] = resolvedRefs[i];
        }
@@ -129,8 +104,8 @@ const TaskBuild = new Lang.Class({
 
        let [tmpPath, stream] = Gio.File.new_tmp("ostbuild-buildroot-XXXXXX.txt");
        let dataOut = Gio.DataOutputStream.new(stream.get_output_stream());
-       for (let i = 0; i < checkoutTrees.length; i++) {
-           let [branch, subpath] = checkoutTrees[i];
+       for (let i = 0; i < rootContents.length; i++) {
+           let [branch, subpath] = rootContents[i];
            let rev = refToRev[branch];
            toChecksumData += refToRev[branch];
            dataOut.put_string(refToRev[branch], cancellable);
@@ -150,9 +125,9 @@ const TaskBuild = new Lang.Class({
             return cachedRoot;
        }
 
-        if (checkoutTrees.length > 0) {
-            print(Format.vprintf("composing buildroot from %d parents (last: %s)", [checkoutTrees.length,
-                                                                                   
checkoutTrees[checkoutTrees.length-1][0]]));
+        if (rootContents.length > 0) {
+            print(Format.vprintf("composing buildroot from %d parents (last: %s)", [rootContents.length,
+                                                                                   
rootContents[rootContents.length-1][0]]));
        }
 
         let cachedRootTmp = cachedRoot.get_parent().get_child(cachedRoot.get_basename() + '.tmp');
@@ -176,6 +151,38 @@ const TaskBuild = new Lang.Class({
         let endtime = GLib.DateTime.new_now_utc();
         print(Format.vprintf("Composed buildroot; %d seconds elapsed", [endtime.difference(starttime) / 
GLib.USEC_PER_SEC]));
         return cachedRoot;
+
+    }, 
+
+    _composeBuildroot: function(workdir, componentName, architecture, cancellable) {
+        let components = this._snapshot.data['components']
+        let component = null;
+        let buildDependencies = [];
+        for (let i = 0; i < components.length; i++) {
+           let component = components[i];
+            if (component['name'] == componentName)
+                break;
+            buildDependencies.push(component);
+       }
+
+        let archBuildrootName = Format.vprintf('%s/bases/%s/%s-devel', [this.osname,
+                                                                       this._snapshot.data['base']['name'],
+                                                                       architecture]);
+
+        print("Computing buildroot contents");
+
+        let archBuildrootRev = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + 
this.repo.get_path(), 'rev-parse',
+                                                                     archBuildrootName], cancellable);
+
+        let rootContents = [[archBuildrootName, '/']];
+        for (let i = 0; i < buildDependencies.length; i++) {
+           let dependency = buildDependencies[i];
+            let buildname = Format.vprintf('%s/components/%s/%s', [this.osname, dependency['name'], 
architecture]);
+            rootContents.push([buildname, '/runtime']);
+            rootContents.push([buildname, '/devel']);
+       }
+
+       return this._composeBuildrootCore(workdir, componentName, architecture, rootContents, cancellable);
      },
 
     _analyzeBuildFailure: function(t, architecture, component, componentSrcdir,
@@ -457,20 +464,27 @@ const TaskBuild = new Lang.Class({
        loop.quit();
     },
 
-    _componentBuildRef: function(component, architecture) {
-        let archBuildname = Format.vprintf('%s/%s', [component['name'], architecture]);
+    _componentBuildRefFromName: function(componentName, architecture) {
+        let archBuildname = Format.vprintf('%s/%s', [componentName, architecture]);
         return this.osname + '/components/' + archBuildname;
     },
 
-    _buildOneComponent: function(component, architecture, cancellable) {
+    _componentBuildRef: function(component, architecture) {
+       return this._componentBuildRefFromName(component['name'], architecture);
+    },
+
+    _buildOneComponent: function(component, architecture, cancellable, params) {
+       params = Params.parse(params, { installedTests: false });
         let basename = component['name'];
 
-        let archBuildname = Format.vprintf('%s/%s', [component['name'], architecture]);
+       if (params.installedTests)
+           basename = basename + '-installed-tests';
+        let archBuildname = Format.vprintf('%s/%s', [basename, architecture]);
         let unixBuildname = archBuildname.replace(/\//g, '_');
         let buildRef = this._componentBuildRef(component, architecture);
 
         let currentVcsVersion = component['revision'];
-        let expandedComponent = this._snapshot.getExpanded(basename);
+        let expandedComponent = this._snapshot.getExpanded(component['name']);
         let previousMetadata = this._componentBuildCache[buildRef];
        let previousBuildVersion = null;
        let previousVcsVersion = null;
@@ -549,7 +563,12 @@ const TaskBuild = new Lang.Class({
         let componentResultdir = buildWorkdir.get_child('results');
         GSystem.file_ensure_directory(componentResultdir, true, cancellable);
 
-        let rootdir = this._composeBuildroot(buildWorkdir, basename, architecture, cancellable);
+       let rootdir;
+       if (params.installedTests)
+           rootdir = this._composeBuildrootCore(buildWorkdir, basename, architecture,
+                                                [[this._installedTestsBuildrootRev, '/']], cancellable);
+       else
+            rootdir = this._composeBuildroot(buildWorkdir, basename, architecture, cancellable);
 
         let tmpdir=buildWorkdir.get_child('tmp');
         GSystem.file_ensure_directory(tmpdir, true, cancellable);
@@ -559,8 +578,20 @@ const TaskBuild = new Lang.Class({
        srcCompileOnePath.copy(destCompileOnePath, Gio.FileCopyFlags.OVERWRITE,
                               cancellable, null);
         GSystem.file_chmod(destCompileOnePath, 493, cancellable);
-        
+
         let chrootSourcedir = Gio.File.new_for_path('/ostbuild/source/' + basename);
+       let chrootChdir = chrootSourcedir;
+
+       let installedTestsSrcdir = componentSrc.get_child('installed-tests');
+       if (params.installedTests) {
+           // We're just building the tests, set our source directory
+           let metaName = '_ostbuild-meta.json';
+           GSystem.file_rename(componentSrc.get_child(metaName), installedTestsSrcdir.get_child(metaName), 
cancellable);
+           chrootChdir = chrootSourcedir.get_child('installed-tests');
+           if (!componentSrc.query_exists(null)) {
+               throw new Error("Component " + basename + " specified with installed tests, but no 
subdirectory found");
+           }
+       }
 
         childArgs = ['setarch', architecture];
         childArgs.push.apply(childArgs, BuildUtil.getBaseUserChrootArgs());
@@ -571,7 +602,7 @@ const TaskBuild = new Lang.Class({
                 '--mount-bind', tmpdir.get_path(), '/tmp',
                 '--mount-bind', componentSrc.get_path(), chrootSourcedir.get_path(),
                 '--mount-bind', componentResultdir.get_path(), '/ostbuild/results',
-                '--chdir', chrootSourcedir.get_path(),
+                '--chdir', chrootChdir.get_path(),
                 rootdir.get_path(), '/ostree-build-compile-one',
                 '--ostbuild-resultdir=/ostbuild/results',
                 '--ostbuild-meta=_ostbuild-meta.json']);
@@ -628,6 +659,36 @@ const TaskBuild = new Lang.Class({
 
         return ostreeRevision;
     },
+    
+    _checkoutOneTreeCore: function(name, composeContents, cancellable) {
+        let composeRootdir = this.subworkdir.get_child(name);
+       GSystem.shutil_rm_rf(composeRootdir, cancellable);
+        GSystem.file_ensure_directory(composeRootdir, true, cancellable);
+
+       let [contentsTmpPath, stream] = Gio.File.new_tmp("ostbuild-compose-XXXXXX.txt");
+       let dataOut = Gio.DataOutputStream.new(stream.get_output_stream());
+       for (let i = 0; i < composeContents.length; i++) {
+           let [branch, subpath] = composeContents[i];
+            dataOut.put_string(branch, cancellable);
+           dataOut.put_byte(0, cancellable);
+            dataOut.put_string(subpath, cancellable);
+           dataOut.put_byte(0, cancellable);
+       }
+        dataOut.close(cancellable);
+
+        ProcUtil.runSync(['ostree', '--repo=' + this.repo.get_path(),
+                         'checkout', '--user-mode', '--union', 
+                         '--from-file=' + contentsTmpPath.get_path(), composeRootdir.get_path()],
+                        cancellable,
+                         {logInitiation: true});
+        GSystem.file_unlink(contentsTmpPath, cancellable);
+
+        let contentsPath = composeRootdir.resolve_relative_path('usr/share/contents.json');
+       GSystem.file_ensure_directory(contentsPath.get_parent(), true, cancellable);
+        JsonUtil.writeJsonFileAtomic(contentsPath, this._snapshot.data, cancellable);
+
+       return composeRootdir;
+    },
 
     _checkoutOneTree: function(target, componentBuildRevs, cancellable) {
         let base = target['base'];
@@ -635,10 +696,6 @@ const TaskBuild = new Lang.Class({
         let runtimeName = this.osname +'/bases/' + base['runtime'];
         let develName = this.osname + '/bases/' + base['devel'];
 
-        let composeRootdir = this.subworkdir.get_child(target['name']);
-       GSystem.shutil_rm_rf(composeRootdir, cancellable);
-        GSystem.file_ensure_directory(composeRootdir, true, cancellable);
-
         let relatedRefs = {};
         let baseRevision = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + this.repo.get_path(),
                                                                  'rev-parse', baseName], cancellable);
@@ -679,26 +736,7 @@ const TaskBuild = new Lang.Class({
            }
        }
 
-       let [contentsTmpPath, stream] = Gio.File.new_tmp("ostbuild-compose-XXXXXX.txt");
-       let dataOut = Gio.DataOutputStream.new(stream.get_output_stream());
-       for (let i = 0; i < composeContents.length; i++) {
-           let [branch, subpath] = composeContents[i];
-            dataOut.put_string(branch, cancellable);
-           dataOut.put_byte(0, cancellable);
-            dataOut.put_string(subpath, cancellable);
-           dataOut.put_byte(0, cancellable);
-       }
-        dataOut.close(cancellable);
-
-        ProcUtil.runSync(['ostree', '--repo=' + this.repo.get_path(),
-                         'checkout', '--user-mode', '--union', 
-                         '--from-file=' + contentsTmpPath.get_path(), composeRootdir.get_path()],
-                        cancellable,
-                         {logInitiation: true});
-        GSystem.file_unlink(contentsTmpPath, cancellable);
-
-        let contentsPath = composeRootdir.resolve_relative_path('usr/share/contents.json');
-        JsonUtil.writeJsonFileAtomic(contentsPath, this._snapshot.data, cancellable);
+       let composeRootdir = this._checkoutOneTreeCore(target['name'], composeContents, cancellable);
 
        let shareOstree = composeRootdir.resolve_relative_path('usr/share/ostree');
        GSystem.file_ensure_directory(shareOstree, true, cancellable);
@@ -710,14 +748,17 @@ const TaskBuild = new Lang.Class({
 
     _commitComposedTree: function(targetName, composeRootdir, relatedTmpPath, cancellable) {
         let treename = this.osname + '/' + targetName;
-        let ostreeRevision = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + 
this.repo.get_path(),
-                                                                   'commit', '-b', treename, '-s', 'Compose',
-                                                                   '--owner-uid=0', '--owner-gid=0', 
'--no-xattrs', 
-                                                                   '--related-objects-file=' + 
relatedTmpPath.get_path(),
-                                                                   '--skip-if-unchanged'], cancellable,
+       let args = ['ostree', '--repo=' + this.repo.get_path(),
+                   'commit', '-b', treename, '-s', 'Compose',
+                   '--owner-uid=0', '--owner-gid=0', '--no-xattrs',
+                   '--skip-if-unchanged'];
+       if (relatedTmpPath !== null)
+           args.push('--related-objects-file=' + relatedTmpPath.get_path());
+       let ostreeRevision = ProcUtil.runSyncGetOutputUTF8Stripped(args, cancellable,
                                                                   {cwd: composeRootdir.get_path(),
                                                                    logInitiation: true});
-        GSystem.file_unlink(relatedTmpPath, cancellable);
+       if (relatedTmpPath !== null)
+            GSystem.file_unlink(relatedTmpPath, cancellable);
         GSystem.shutil_rm_rf(composeRootdir, cancellable);
        return [treename, ostreeRevision];
     },
@@ -964,17 +1005,22 @@ const TaskBuild = new Lang.Class({
 
         let runtimeComponents = [];
         let develComponents = [];
+        let testingComponents = [];
 
         for (let i = 0; i < components.length; i++) {
            let component = components[i];
             let name = component['name']
 
             let isRuntime = (component['component'] || 'runtime') == 'runtime';
+            let isTesting = (component['component'] || 'runtime') == 'testing';
 
             if (isRuntime) {
                 runtimeComponents.push(component);
            }
-            develComponents.push(component);
+           if (isTesting)
+               testingComponents.push(component);
+           else
+               develComponents.push(component);
 
            let isNoarch = component['noarch'] || false;
            let componentArches;
@@ -1091,9 +1137,11 @@ const TaskBuild = new Lang.Class({
        }
 
        let targetRevisions = {};
+       let finalInstalledTestRevisions = {};
        let buildData = { snapshotName: this._snapshot.path.get_basename(),
                          snapshot: this._snapshot.data,
                          targets: targetRevisions };
+       buildData['installed-tests'] = finalInstalledTestRevisions;
 
        // First loop over the -devel trees per architecture, and
        // generate an initramfs.
@@ -1123,6 +1171,9 @@ const TaskBuild = new Lang.Class({
            GSystem.file_linkcopy(initramfsPath, targetInitramfsPath, Gio.FileCopyFlags.ALL_METADATA, 
cancellable);
            let [treename, ostreeRev] = this._commitComposedTree(develTargetName, composeRootdir, 
relatedTmpPath, cancellable);
            targetRevisions[treename] = ostreeRev;
+           // Also note the revision of this, since it will be used
+           // as the buildroot for installed tests
+           this._installedTestsBuildrootRev = ostreeRev;
        }
 
        // Now loop over the other targets per architecture, reusing
@@ -1143,6 +1194,47 @@ const TaskBuild = new Lang.Class({
                targetRevisions[treename] = ostreeRev;
            }
        }
+
+       let installedTestComponentNames = this._snapshot.data['installed-tests-components'] || [];
+       print("Using installed test components: " + installedTestComponentNames.join(', '));
+       let installedTestRevs = {};
+        for (let i = 0; i < architectures.length; i++) {
+           installedTestRevs[architectures[i]] = [];
+       }
+       for (let i = 0; i < testingComponents.length; i++) {
+           let component = testingComponents[i];
+           let name = component['name'];
+            for (let j = 0; j < architectures.length; j++) {
+               let architecture = architectures[j];
+               let archname = component['name'] + '/' + architecture;
+               let rev = componentBuildRevs[archname];
+               if (!rev)
+                   throw new Error("no build for " + buildRef);
+               installedTestRevs[architecture].push(rev);
+           }
+       }
+        for (let i = 0; i < installedTestComponentNames.length; i++) {
+           let componentName = installedTestComponentNames[i];
+            for (let j = 0; j < architectures.length; j++) {
+               let architecture = architectures[j];
+               let archname = componentName + '-installed-tests' + '/' + architecture;
+               let component = this._snapshot.getComponent(componentName);
+               let buildRev = this._buildOneComponent(component, architecture, cancellable, { 
installedTests: true });
+               installedTestRevs[architecture].push(buildRev);
+           }
+       }
+       for (let architecture in installedTestRevs) {
+           let rootName = 'buildmaster/' + architecture + '-installed-tests';
+           let composeContents = [];
+           let revs = installedTestRevs[architecture];
+            for (let j = 0; j < revs.length; j++) {
+               composeContents.push([revs[j], '/runtime']);
+           }
+           let composeRootdir = this._checkoutOneTreeCore(rootName, composeContents, cancellable);
+           let [treename, rev] = this._commitComposedTree(rootName, composeRootdir, null, cancellable);
+           finalInstalledTestRevisions[treename] = rev;
+       }
+
        let [path, modified] = builddb.store(buildData, cancellable);
        print("Build complete: " + path.get_path());
     }
diff --git a/src/js/tasks/task-integrationtest.js b/src/js/tasks/task-integrationtest.js
new file mode 100644
index 0000000..c090ff0
--- /dev/null
+++ b/src/js/tasks/task-integrationtest.js
@@ -0,0 +1,69 @@
+// -*- 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 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;
+
+const TaskIntegrationTest = new Lang.Class({
+    Name: 'TaskIntegrationTest',
+    Extends: TestBase.TestBase,
+
+    TaskName: "integrationtest",
+    TaskAfter: ['smoketest'],
+
+    RequiredMessageIDs: ["4d013788dd704743b826436c951e551d" // Tests succeeded
+                        ],
+
+    FailedMessageIDs:   ["10dd2dc188b54a5e98970f56499d1f73", // gnome-session required component failed
+                         "0eee66bf98514369bef9868327a43cf1" // Tests failed
+                        ],
+
+    CompletedTag: 'integrated',
+
+    _prepareDisk: function(mntdir, arch, cancellable) {
+        let osname = this._buildData['snapshot']['osname'];
+        let [deployDir, deployEtcDir] = LibQA.getDeployDirs(mntdir, osname);
+        let installedTestsName = osname + '/buildmaster/' +arch + '-installed-tests';
+        let installedTestsRev = this._buildData['installed-tests'][installedTestsName];
+        if (!installedTestsRev)
+            throw new Error("No installed tests rev for " + installedTestsName);
+        ProcUtil.runSync(['ostree', '--repo=' + this.repo.get_path(),
+                          'checkout', '--no-triggers', '--user-mode', '--union', installedTestsRev, 
deployDir.get_path()], cancellable,
+                         { logInitiation: true });
+        // A bit of a hack; we need to sync the data in /etc/ from the tree to the real /etc.
+        // Probably this should have a real ostree command ("ostree admin overlay"?)
+        let relpath = 'xdg/autostart/gnome-desktop-testing.desktop';
+        let src = deployDir.resolve_relative_path('etc/' + relpath);
+        let dest = deployEtcDir.resolve_relative_path(relpath);
+        GSystem.file_linkcopy(src, dest, Gio.FileCopyFlags.OVERWRITE,
+                              cancellable);
+    }
+});
diff --git a/src/js/tasks/task-smoketest.js b/src/js/tasks/task-smoketest.js
index d82aff3..2b5f80f 100644
--- a/src/js/tasks/task-smoketest.js
+++ b/src/js/tasks/task-smoketest.js
@@ -34,7 +34,7 @@ const JSONUtil = imports.jsonutil;
 
 const TaskSmoketest = new Lang.Class({
     Name: 'TaskSmoketest',
-    Extends: TestBase.TaskTestBase,
+    Extends: TestBase.TestBase,
 
     TaskName: "smoketest",
     TaskAfter: ['builddisks'],
@@ -43,5 +43,7 @@ const TaskSmoketest = new Lang.Class({
                         ],
 
     FailedMessageIDs: ["10dd2dc188b54a5e98970f56499d1f73" // gnome-session required component failed
-                      ]
+                      ],
+
+    CompletedTag: 'smoketested'
 });
diff --git a/src/js/tasks/testbase.js b/src/js/tasks/testbase.js
index 33a31c6..06c5030 100644
--- a/src/js/tasks/testbase.js
+++ b/src/js/tasks/testbase.js
@@ -29,6 +29,7 @@ const ProcUtil = imports.procutil;
 const Task = imports.task;
 const LibQA = imports.libqa;
 const JSUtil = imports.jsutil;
+const JSONUtil = imports.jsonutil;
 
 const TIMEOUT_SECONDS = 10 * 60;
 const COMPLETE_IDLE_WAIT_SECONDS = 10;
@@ -36,7 +37,8 @@ const COMPLETE_IDLE_WAIT_SECONDS = 10;
 const TestOneDisk = new Lang.Class({
     Name: 'TestOneDisk',
 
-    _init: function(testRequiredMessageIds, testFailedMessageIds) {
+    _init: function(parentTask, testRequiredMessageIds, testFailedMessageIds) {
+        this._parentTask = parentTask;
         this._testRequiredMessageIds = testRequiredMessageIds;
         this._testFailedMessageIds = testFailedMessageIds;
     },
@@ -214,8 +216,10 @@ const TestOneDisk = new Lang.Class({
         this._loop.quit();
     },
 
-    execute: function(subworkdir, diskPath, cancellable) {
+    execute: function(subworkdir, buildData, repo, diskPath, cancellable) {
         print("Testing disk " + diskPath.get_path());
+        this._buildData = buildData;
+        this._repo = repo;
         this._subworkdir = subworkdir;
         this._loop = GLib.MainLoop.new(null, true);
         this._foundAllMessageIds = false;
@@ -240,6 +244,12 @@ const TestOneDisk = new Lang.Class({
         }
         this._cancellable = cancellable;
 
+        // HACK
+        if (diskPath.get_basename().indexOf('x86_64') >= 0)
+            this._diskArch = 'x86_64';
+        else
+            this._diskArch = 'i686';
+
         let qemuArgs = [LibQA.getQemuPath()];
         qemuArgs.push.apply(qemuArgs, LibQA.DEFAULT_QEMU_OPTS);
 
@@ -256,6 +266,8 @@ const TestOneDisk = new Lang.Class({
             LibQA.injectExportJournal(currentDir, currentEtcDir, cancellable);
             LibQA.injectTestUserCreation(currentDir, currentEtcDir, 'testuser', {}, cancellable);
             LibQA.enableAutologin(currentDir, currentEtcDir, 'testuser', cancellable);
+
+            this._parentTask._prepareDisk(mntdir, this._diskArch, cancellable);
         } finally {
             gfmnt.umount(cancellable);
         }
@@ -305,7 +317,7 @@ const TestOneDisk = new Lang.Class({
     }
 });
 
-const TaskTestBase = new Lang.Class({
+const TestBase = new Lang.Class({
     Name: 'TestBase',
     Extends: Task.TaskDef,
 
@@ -322,6 +334,12 @@ const TaskTestBase = new Lang.Class({
     RequiredMessageIDs: [],
     FailedMessageIDs: [],
 
+    CompletedTag: null,
+
+    _prepareDisk: function(mntdir, cancellable) {
+        // Nothing, intended for subclasses
+    },
+
     execute: function(cancellable) {
              let imageDir = this.workdir.get_child('images');
              let currentImages = imageDir.get_child('current');
@@ -330,6 +348,7 @@ const TaskTestBase = new Lang.Class({
                                                  cancellable);
         let info;
         let buildJson;
+        let disksToTest = [];
         while ((info = e.next_file(cancellable)) != null) {
             let name = info.get_name();
             if (name.indexOf('build-') == 0 && JSUtil.stringEndswith(name, '.json')) {
@@ -338,21 +357,29 @@ const TaskTestBase = new Lang.Class({
             }
             if (!JSUtil.stringEndswith(name, '.qcow2'))
                 continue;
+            disksToTest.push(name);
+        }
+        e.close(null);
+        this._buildData = null;
+        if (buildJson != null)
+            this._buildData = JSONUtil.loadJson(buildJson, cancellable);
+        for (let i = 0; i < disksToTest.length; i++) {
+            let name = disksToTest[i];
             let workdirName = 'work-' + name.replace(/\.qcow2$/, '');
             let subworkdir = Gio.File.new_for_path(workdirName);
             GSystem.file_ensure_directory(subworkdir, true, cancellable);
-            let test = new TestOneDisk(this.BaseRequiredMessageIDs.concat(this.RequiredMessageIDs),
+            let test = new TestOneDisk(this, this.BaseRequiredMessageIDs.concat(this.RequiredMessageIDs),
                                        this.BaseFailedMessageIDs.concat(this.FailedMessageIDs));
-            test.execute(subworkdir, currentImages.get_child(name), cancellable);
+            test.execute(subworkdir, this._buildData, this.repo, currentImages.get_child(name), cancellable);
         }
-        if (buildJson != null) {
-            let buildData = JSONUtil.loadJson(buildJson, cancellable);
+        let buildData = this._buildData;
+        if (buildJson != null && this.CompletedTag !== null) {
             let refData = '';
             let snapshot = buildData['snapshot'];
             for (let targetName in buildData['targets']) {
                 let targetRev = buildData['targets'][targetName];
                 let lastSlash = targetName.lastIndexOf('/');
-                let smoketestedRef = snapshot['osname'] + '/smoketested' + targetName.substr(lastSlash);
+                let smoketestedRef = snapshot['osname'] + '/' + this.CompletedTag + 
targetName.substr(lastSlash);
                 refData += smoketestedRef + ' ' + targetRev + '\n';
             }
             ProcUtil.runProcWithInputSyncGetLines(['ostree', '--repo=' + this.repo.get_path(),


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]