Merge lp:~elopio/snappy/15.04-backport-integration into lp:~snappy-dev/snappy/15.04-deprecated

Proposed by Leo Arias
Status: Merged
Approved by: Leo Arias
Approved revision: 469
Merged at revision: 466
Proposed branch: lp:~elopio/snappy/15.04-backport-integration
Merge into: lp:~snappy-dev/snappy/15.04-deprecated
Prerequisite: lp:~elopio/snappy/backport-gocheck
Diff against target: 2812 lines (+2596/-3)
34 files modified
_integration-tests/README.md (+117/-0)
_integration-tests/data/snaps/basic/meta/package.yaml (+4/-0)
_integration-tests/data/snaps/basic/meta/readme.md (+3/-0)
_integration-tests/data/snaps/missing-readme/meta/package.yaml (+4/-0)
_integration-tests/data/snaps/wrong-yaml/meta/package.yaml (+5/-0)
_integration-tests/data/snaps/wrong-yaml/meta/readme.md (+3/-0)
_integration-tests/data/tpl/control (+4/-0)
_integration-tests/main.go (+105/-0)
_integration-tests/reboot-wrapper (+35/-0)
_integration-tests/tests/apt_test.go (+39/-0)
_integration-tests/tests/base_test.go (+29/-0)
_integration-tests/tests/build_test.go (+90/-0)
_integration-tests/tests/failover_rclocal_crash_test.go (+54/-0)
_integration-tests/tests/failover_systemd_loop_test.go (+114/-0)
_integration-tests/tests/failover_test.go (+58/-0)
_integration-tests/tests/failover_zero_size_file_test.go (+258/-0)
_integration-tests/tests/info_test.go (+79/-0)
_integration-tests/tests/installApp_test.go (+128/-0)
_integration-tests/tests/installFramework_test.go (+93/-0)
_integration-tests/tests/list_test.go (+78/-0)
_integration-tests/tests/rollback_test.go (+53/-0)
_integration-tests/tests/search_test.go (+44/-0)
_integration-tests/tests/update_test.go (+51/-0)
_integration-tests/tests/writablePaths_test.go (+106/-0)
_integration-tests/testutils/autopkgtest/autopkgtest.go (+111/-0)
_integration-tests/testutils/autopkgtest/ssh.go (+46/-0)
_integration-tests/testutils/build/build.go (+76/-0)
_integration-tests/testutils/common/common.go (+379/-0)
_integration-tests/testutils/common/common_test.go (+68/-0)
_integration-tests/testutils/config/config.go (+75/-0)
_integration-tests/testutils/config/config_test.go (+121/-0)
_integration-tests/testutils/image/image.go (+86/-0)
_integration-tests/testutils/testutils.go (+61/-0)
run-checks (+19/-3)
To merge this branch: bzr merge lp:~elopio/snappy/15.04-backport-integration
Reviewer Review Type Date Requested Status
Federico Gimenez (community) Approve
Review via email: mp+266664@code.launchpad.net

This proposal supersedes a proposal from 2015-07-27.

Commit message

Backported the integration tests.

Description of the change

Huge branch! But you don't need to check everything, as it's just a copy from trunk. Run the tests and we are good to go.

To post a comment you must log in.
Revision history for this message
Leo Arias (elopio) wrote : Posted in a previous version of this proposal

Work in progress because we want to land this after the current release. But this is working, ready to be tested.

Revision history for this message
Federico Gimenez (fgimenez) wrote : Posted in a previous version of this proposal

Thanks for this Leo, leaving my approval here

review: Approve (tested)
Revision history for this message
Federico Gimenez (fgimenez) wrote : Posted in a previous version of this proposal

There's a problem with the gocheck dependency, the installed package name doesn't match the import name [1]. We should modify also dependencies.tsv.

Thanks!

[1] http://10.55.61.165:8080/job/snappy-integration-tests-canonistack-1504-edge/14/console

Revision history for this message
Leo Arias (elopio) wrote :

@Federico, I backported the gocheck update because we need a newer version than the one that's in launchpad. It's a prerequisite of this branch.

Revision history for this message
Federico Gimenez (fgimenez) wrote :

Ok, tested and working, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '_integration-tests'
2=== added file '_integration-tests/README.md'
3--- _integration-tests/README.md 1970-01-01 00:00:00 +0000
4+++ _integration-tests/README.md 2015-08-02 12:28:52 +0000
5@@ -0,0 +1,117 @@
6+# Integration testing for snappy
7+
8+## Requirements
9+
10+ * autopkgtest (>= 3.15.1)
11+
12+ Get the latest autopkgtest deb from
13+ https://packages.debian.org/sid/all/autopkgtest/download
14+
15+ * Internet access in the test bed.
16+
17+## Setting up the project
18+
19+First you need to set up the GOPATH, get the snappy sources and the
20+dependencies as explained in the `README.md` that is located at the root of the
21+branch.
22+
23+## Testing a virtual machine
24+
25+You can execute the full integration suite in a local virtual machine with:
26+
27+ go run _integration-tests/main.go
28+
29+The test runner will create the snappy images with `ubuntu-device-flash`, so it
30+will ask for your password to run this command with `sudo`.
31+
32+You can also especify more options to customize the image being created, including
33+the release, the channel and the revision to use. This parameters will be passed
34+to `ubuntu-device-flash`:
35+
36+ go run _integration-tests/main.go -release 15.04 -channel stable -revision 3
37+
38+The default values are suited to testing the most recent version, `rolling` for
39+release, `edge` for channel and an empty revision, which picks the latest
40+available.
41+
42+## Testing snappy from a branch
43+
44+With the --snappy-from-branch flag, the snappy CLI command will be compiled
45+from the current branch, copied to the test bed and used during the integration
46+tests:
47+
48+ go run _integration-tests/main.go --snappy-from-branch
49+
50+You can use this flag to test in a remote machine too.
51+
52+## Filtering the tests to run
53+
54+With the --filter flag you can select the tests to run. For instance you can
55+pass MyTestSuite, MyTestSuite.FirstCustomTest or MyTestSuite.*CustomTest:
56+
57+ go run _integration-tests/main.go --filter MyTestSuite.FirstCustomTest
58+
59+## Testing a remote machine
60+
61+You can execute the integration suite in a remote snappy machine with:
62+
63+ go run _integration-tests/main.go --ip {testbed-ip} --port {testbed-port} \
64+ --arch {testbed-arch}
65+
66+The test runner will use `ssh-copy-id` to send your identity file to the
67+testbed, so it will ask for the password of the ubuntu user in the test bed.
68+
69+When running in a remote machine, the test runner assumes the test bed is in
70+the latest rolling edge version, and it will skip all the tests that
71+require a different version. See the following section for instructions for
72+setting up a BeagleBone Black as the test bed.
73+
74+## Testing a BeagleBone Black
75+
76+First flash the latest 15.04 edge version into the sd card
77+(replacing /dev/sdX with the path to your card):
78+
79+ sudo ubuntu-device-flash core 15.04 --channel edge --oem beagleblack \
80+ --developer-mode --enable-ssh -o ubuntu-15.04-edge-armhf-bbb.img
81+
82+ sudo dd if=ubuntu-15.04-edge-armhf-bbb.img of=/dev/sdX bs=32M
83+ sync
84+
85+Then boot the board with the sd card, make sure that it is connected to the
86+same network as the test runner host, and find the {beaglebone-ip}.
87+
88+Run the tests with:
89+
90+ go run _integration-tests/main.go --ip {beaglebone-ip} --arch arm
91+
92+## Testing an update
93+
94+With the --update flag you can flash an old image, update to the latest and
95+then run the whole suite on the updated system. The release, the channel and
96+the revision flags specify the image that will be flashed, and the
97+target-release and target-channel flags specify the values to be used in the
98+update if they are different from the flashed values.
99+
100+For example, to update from 15.04 edge -1 to the latest and then run the
101+integration tests:
102+
103+ go run _integration-tests/main.go --snappy-from-branch \
104+ --release=15.04 --revision=-1 --update
105+
106+To update from 15.04 alpha to 15.04 edge and then run the integration tests:
107+
108+ go run _integration-tests/main.go --snappy-from-branch \
109+ --release=15.04 --channel=alpha \
110+ --update --target-channel=edge
111+
112+## Testing a rollback
113+
114+With the --rollback flag you can flash an old image, update to the latest,
115+rollback again to the old image and then run the whole suite on the rolled
116+back system. You should use the release, channel, revision, target-release and
117+target-channel flags as when testing an update.
118+
119+For example, to test a rollback from latest 15.04 edge to 15.04 edge -1:
120+
121+ go run _integration-tests/main.go \
122+ --release=15.04 --revision=-1 --rollback
123
124=== added directory '_integration-tests/data'
125=== added directory '_integration-tests/data/snaps'
126=== added directory '_integration-tests/data/snaps/basic'
127=== added directory '_integration-tests/data/snaps/basic/meta'
128=== added file '_integration-tests/data/snaps/basic/meta/package.yaml'
129--- _integration-tests/data/snaps/basic/meta/package.yaml 1970-01-01 00:00:00 +0000
130+++ _integration-tests/data/snaps/basic/meta/package.yaml 2015-08-02 12:28:52 +0000
131@@ -0,0 +1,4 @@
132+name: basic
133+version: 1.0
134+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
135+icon: meta/snappy64.png
136
137=== added file '_integration-tests/data/snaps/basic/meta/readme.md'
138--- _integration-tests/data/snaps/basic/meta/readme.md 1970-01-01 00:00:00 +0000
139+++ _integration-tests/data/snaps/basic/meta/readme.md 2015-08-02 12:28:52 +0000
140@@ -0,0 +1,3 @@
141+Basic snap
142+
143+A basic buildable snap
144
145=== added file '_integration-tests/data/snaps/basic/meta/snappy64.png'
146Binary files _integration-tests/data/snaps/basic/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/basic/meta/snappy64.png 2015-08-02 12:28:52 +0000 differ
147=== added directory '_integration-tests/data/snaps/missing-readme'
148=== added directory '_integration-tests/data/snaps/missing-readme/meta'
149=== added file '_integration-tests/data/snaps/missing-readme/meta/package.yaml'
150--- _integration-tests/data/snaps/missing-readme/meta/package.yaml 1970-01-01 00:00:00 +0000
151+++ _integration-tests/data/snaps/missing-readme/meta/package.yaml 2015-08-02 12:28:52 +0000
152@@ -0,0 +1,4 @@
153+name: missing-readme
154+version: 1.0
155+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
156+icon: meta/snappy64.png
157
158=== added file '_integration-tests/data/snaps/missing-readme/meta/snappy64.png'
159Binary files _integration-tests/data/snaps/missing-readme/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/missing-readme/meta/snappy64.png 2015-08-02 12:28:52 +0000 differ
160=== added directory '_integration-tests/data/snaps/wrong-yaml'
161=== added directory '_integration-tests/data/snaps/wrong-yaml/meta'
162=== added file '_integration-tests/data/snaps/wrong-yaml/meta/package.yaml'
163--- _integration-tests/data/snaps/wrong-yaml/meta/package.yaml 1970-01-01 00:00:00 +0000
164+++ _integration-tests/data/snaps/wrong-yaml/meta/package.yaml 2015-08-02 12:28:52 +0000
165@@ -0,0 +1,5 @@
166+# This is an invalid yaml, there's a missing colon after the 'name' field
167+name wrong-yaml
168+version: 1.0
169+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
170+icon: meta/snappy64.png
171
172=== added file '_integration-tests/data/snaps/wrong-yaml/meta/readme.md'
173--- _integration-tests/data/snaps/wrong-yaml/meta/readme.md 1970-01-01 00:00:00 +0000
174+++ _integration-tests/data/snaps/wrong-yaml/meta/readme.md 2015-08-02 12:28:52 +0000
175@@ -0,0 +1,3 @@
176+Wrong metadata snap
177+
178+A snap with an invalid meta/package.yaml
179
180=== added file '_integration-tests/data/snaps/wrong-yaml/meta/snappy64.png'
181Binary files _integration-tests/data/snaps/wrong-yaml/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/wrong-yaml/meta/snappy64.png 2015-08-02 12:28:52 +0000 differ
182=== added directory '_integration-tests/data/tpl'
183=== added file '_integration-tests/data/tpl/control'
184--- _integration-tests/data/tpl/control 1970-01-01 00:00:00 +0000
185+++ _integration-tests/data/tpl/control 2015-08-02 12:28:52 +0000
186@@ -0,0 +1,4 @@
187+{{ $filter := .Filter }}
188+{{ $test := .Test }}
189+Test-Command: ./_integration-tests/reboot-wrapper {{ $test }} {{ if $filter }}-gocheck.f {{ $filter }}{{ end }}
190+Restrictions: allow-stderr
191
192=== added file '_integration-tests/main.go'
193--- _integration-tests/main.go 1970-01-01 00:00:00 +0000
194+++ _integration-tests/main.go 2015-08-02 12:28:52 +0000
195@@ -0,0 +1,105 @@
196+// -*- Mode: Go; indent-tabs-mode: t -*-
197+
198+/*
199+ * Copyright (C) 2015 Canonical Ltd
200+ *
201+ * This program is free software: you can redistribute it and/or modify
202+ * it under the terms of the GNU General Public License version 3 as
203+ * published by the Free Software Foundation.
204+ *
205+ * This program is distributed in the hope that it will be useful,
206+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
207+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
208+ * GNU General Public License for more details.
209+ *
210+ * You should have received a copy of the GNU General Public License
211+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
212+ *
213+ */
214+
215+package main
216+
217+import (
218+ "flag"
219+ "log"
220+ "os"
221+ "path/filepath"
222+ "strconv"
223+
224+ "launchpad.net/snappy/_integration-tests/testutils"
225+ "launchpad.net/snappy/_integration-tests/testutils/autopkgtest"
226+ "launchpad.net/snappy/_integration-tests/testutils/build"
227+ "launchpad.net/snappy/_integration-tests/testutils/config"
228+ "launchpad.net/snappy/_integration-tests/testutils/image"
229+)
230+
231+const (
232+ baseDir = "/tmp/snappy-test"
233+ defaultRelease = "rolling"
234+ defaultChannel = "edge"
235+ defaultSSHPort = 22
236+ dataOutputDir = "_integration-tests/data/output/"
237+)
238+
239+var configFileName = filepath.Join(dataOutputDir, "testconfig.json")
240+
241+func main() {
242+ var (
243+ useSnappyFromBranch = flag.Bool("snappy-from-branch", false,
244+ "If this flag is used, snappy will be compiled from this branch, copied to the testbed and used for the tests. Otherwise, the snappy installed with the image will be used.")
245+ arch = flag.String("arch", "",
246+ "Architecture of the test bed. Defaults to use the same architecture as the host.")
247+ testbedIP = flag.String("ip", "",
248+ "IP of the testbed. If no IP is passed, a virtual machine will be created for the test.")
249+ testbedPort = flag.Int("port", defaultSSHPort,
250+ "SSH port of the testbed. Defaults to use port "+strconv.Itoa(defaultSSHPort))
251+ testFilter = flag.String("filter", "",
252+ "Suites or tests to run, for instance MyTestSuite, MyTestSuite.FirstCustomTest or MyTestSuite.*CustomTest")
253+ imgRelease = flag.String("release", defaultRelease,
254+ "Release of the image to be built, defaults to "+defaultRelease)
255+ imgChannel = flag.String("channel", defaultChannel,
256+ "Channel of the image to be built, defaults to "+defaultChannel)
257+ imgRevision = flag.String("revision", "",
258+ "Revision of the image to be built (can be relative to the latest available revision in the given release and channel as in -1), defaults to the empty string")
259+ update = flag.Bool("update", false,
260+ "If this flag is used, the image will be updated before running the tests.")
261+ targetRelease = flag.String("target-release", "",
262+ "If the update flag is used, the image will be updated to this release before running the tests.")
263+ targetChannel = flag.String("target-channel", "",
264+ "If the update flag is used, the image will be updated to this channel before running the tests.")
265+ rollback = flag.Bool("rollback", false,
266+ "If this flag is used, the image will be updated and then rolled back before running the tests.")
267+ )
268+
269+ flag.Parse()
270+
271+ build.Assets(*useSnappyFromBranch, *arch)
272+
273+ // TODO: generate the files out of the source tree. --elopio - 2015-07-15
274+ testutils.PrepareTargetDir(dataOutputDir)
275+ defer os.RemoveAll(dataOutputDir)
276+
277+ remoteTestbed := *testbedIP != ""
278+
279+ // TODO: pass the config as arguments to the test binaries.
280+ // --elopio - 2015-07-15
281+ cfg := config.NewConfig(
282+ configFileName, *imgRelease, *imgChannel, *targetRelease, *targetChannel,
283+ remoteTestbed, *update, *rollback)
284+ cfg.Write()
285+
286+ rootPath := testutils.RootPath()
287+
288+ test := autopkgtest.NewAutopkgtest(rootPath, baseDir, *testFilter, build.IntegrationTestName)
289+ if !remoteTestbed {
290+ img := image.NewImage(*imgRelease, *imgChannel, *imgRevision, baseDir)
291+
292+ if imagePath, err := img.UdfCreate(); err == nil {
293+ test.AdtRunLocal(imagePath)
294+ } else {
295+ log.Panic(err.Error())
296+ }
297+ } else {
298+ test.AdtRunRemote(*testbedIP, *testbedPort)
299+ }
300+}
301
302=== added file '_integration-tests/reboot-wrapper'
303--- _integration-tests/reboot-wrapper 1970-01-01 00:00:00 +0000
304+++ _integration-tests/reboot-wrapper 2015-08-02 12:28:52 +0000
305@@ -0,0 +1,35 @@
306+#!/bin/sh
307+
308+# Copyright (C) 2015 Canonical Ltd
309+#
310+# This program is free software: you can redistribute it and/or modify
311+# it under the terms of the GNU General Public License version 3 as
312+# published by the Free Software Foundation.
313+#
314+# This program is distributed in the hope that it will be useful,
315+# but WITHOUT ANY WARRANTY; without even the implied warranty of
316+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
317+# GNU General Public License for more details.
318+#
319+# You should have received a copy of the GNU General Public License
320+# along with this program. If not, see <http://www.gnu.org/licenses/>.
321+
322+# This runs the $1 go test binary and reboots if necessary.
323+
324+set -e
325+
326+TEST=$1
327+NEEDS_REBOOT=/tmp/needs-reboot
328+
329+export PATH=$(pwd)/_integration-tests/bin:$PATH
330+
331+# shift to remove the test binary name (first argument) and be able to pass the rest
332+# of them to it
333+shift
334+${TEST} -check.vv -test.outputdir=$ADT_ARTIFACTS-$ADT_REBOOT_MARK "$@"
335+
336+if [ -e ${NEEDS_REBOOT} ]; then
337+ mark=`cat ${NEEDS_REBOOT}`
338+ echo "Rebooting..."
339+ sudo /tmp/autopkgtest-reboot "$mark"
340+fi
341
342=== added directory '_integration-tests/tests'
343=== added file '_integration-tests/tests/apt_test.go'
344--- _integration-tests/tests/apt_test.go 1970-01-01 00:00:00 +0000
345+++ _integration-tests/tests/apt_test.go 2015-08-02 12:28:52 +0000
346@@ -0,0 +1,39 @@
347+// -*- Mode: Go; indent-tabs-mode: t -*-
348+
349+/*
350+ * Copyright (C) 2015 Canonical Ltd
351+ *
352+ * This program is free software: you can redistribute it and/or modify
353+ * it under the terms of the GNU General Public License version 3 as
354+ * published by the Free Software Foundation.
355+ *
356+ * This program is distributed in the hope that it will be useful,
357+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
358+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
359+ * GNU General Public License for more details.
360+ *
361+ * You should have received a copy of the GNU General Public License
362+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
363+ *
364+ */
365+
366+package tests
367+
368+import (
369+ . "launchpad.net/snappy/_integration-tests/testutils/common"
370+
371+ check "gopkg.in/check.v1"
372+)
373+
374+var _ = check.Suite(&aptSuite{})
375+
376+type aptSuite struct {
377+ SnappySuite
378+}
379+
380+func (s *aptSuite) TestAptGetMustPrintError(c *check.C) {
381+ aptOutput := ExecCommand(c, "apt-get", "update")
382+
383+ expected := "Ubuntu Core does not use apt-get, see 'snappy --help'!\n"
384+ c.Assert(aptOutput, check.Equals, expected)
385+}
386
387=== added file '_integration-tests/tests/base_test.go'
388--- _integration-tests/tests/base_test.go 1970-01-01 00:00:00 +0000
389+++ _integration-tests/tests/base_test.go 2015-08-02 12:28:52 +0000
390@@ -0,0 +1,29 @@
391+// -*- Mode: Go; indent-tabs-mode: t -*-
392+
393+/*
394+ * Copyright (C) 2015 Canonical Ltd
395+ *
396+ * This program is free software: you can redistribute it and/or modify
397+ * it under the terms of the GNU General Public License version 3 as
398+ * published by the Free Software Foundation.
399+ *
400+ * This program is distributed in the hope that it will be useful,
401+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
402+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
403+ * GNU General Public License for more details.
404+ *
405+ * You should have received a copy of the GNU General Public License
406+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
407+ *
408+ */
409+
410+package tests
411+
412+import (
413+ "testing"
414+
415+ . "gopkg.in/check.v1"
416+)
417+
418+// Hook up gocheck into the "go test" runner.
419+func Test(t *testing.T) { TestingT(t) }
420
421=== added file '_integration-tests/tests/build_test.go'
422--- _integration-tests/tests/build_test.go 1970-01-01 00:00:00 +0000
423+++ _integration-tests/tests/build_test.go 2015-08-02 12:28:52 +0000
424@@ -0,0 +1,90 @@
425+// -*- Mode: Go; indent-tabs-mode: t -*-
426+
427+/*
428+ * Copyright (C) 2015 Canonical Ltd
429+ *
430+ * This program is free software: you can redistribute it and/or modify
431+ * it under the terms of the GNU General Public License version 3 as
432+ * published by the Free Software Foundation.
433+ *
434+ * This program is distributed in the hope that it will be useful,
435+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
436+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
437+ * GNU General Public License for more details.
438+ *
439+ * You should have received a copy of the GNU General Public License
440+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
441+ *
442+ */
443+
444+package tests
445+
446+import (
447+ "fmt"
448+ "os"
449+ "os/exec"
450+
451+ . "launchpad.net/snappy/_integration-tests/testutils/common"
452+
453+ . "gopkg.in/check.v1"
454+)
455+
456+const (
457+ baseSnapPath = "_integration-tests/data/snaps"
458+ basicSnapName = "basic"
459+ wrongYamlSnapName = "wrong-yaml"
460+ missingReadmeSnapName = "missing-readme"
461+)
462+
463+var _ = Suite(&buildSuite{})
464+
465+type buildSuite struct {
466+ SnappySuite
467+}
468+
469+func buildSnap(c *C, snapPath string) string {
470+ return ExecCommand(c, "snappy", "build", snapPath)
471+}
472+
473+func (s *buildSuite) TestBuildBasicSnapOnSnappy(c *C) {
474+ // build basic snap and check output
475+ snapPath := baseSnapPath + "/" + basicSnapName
476+ buildOutput := buildSnap(c, snapPath)
477+ snapName := basicSnapName + "_1.0_all.snap"
478+ expected := fmt.Sprintf("Generated '%s' snap\n", snapName)
479+ c.Check(buildOutput, Equals, expected)
480+ defer os.Remove(snapPath + "/" + snapName)
481+
482+ // install built snap and check output
483+ installOutput := InstallSnap(c, snapName)
484+ defer RemoveSnap(c, basicSnapName)
485+ expected = "(?ms)" +
486+ "Installing " + snapName + "\n" +
487+ ".*Signature check failed, but installing anyway as requested\n" +
488+ "Name +Date +Version +Developer \n" +
489+ ".*\n" +
490+ basicSnapName + " +.* +.* +sideload \n" +
491+ ".*\n"
492+
493+ c.Check(installOutput, Matches, expected)
494+
495+ // teardown, remove snap file
496+ c.Assert(os.Remove(snapName), IsNil, Commentf("Error removing %s", snapName))
497+}
498+
499+func (s *buildSuite) TestBuildWrongYamlSnapOnSnappy(c *C) {
500+ commonWrongTest(c, wrongYamlSnapName, "(?msi).*Can not parse.*yaml: line 2: mapping values are not allowed in this context.*")
501+}
502+
503+func (s *buildSuite) TestBuildMissingReadmeSnapOnSnappy(c *C) {
504+ commonWrongTest(c, missingReadmeSnapName, ".*readme.md: no such file or directory\n")
505+}
506+
507+func commonWrongTest(c *C, testName, expected string) {
508+ // build wrong snap and check output
509+ cmd := exec.Command("snappy", "build", fmt.Sprintf("%s/%s", baseSnapPath, testName))
510+ echoOutput, err := cmd.CombinedOutput()
511+ c.Assert(err, NotNil, Commentf("%s should not be built", testName))
512+
513+ c.Assert(string(echoOutput), Matches, expected)
514+}
515
516=== added file '_integration-tests/tests/failover_rclocal_crash_test.go'
517--- _integration-tests/tests/failover_rclocal_crash_test.go 1970-01-01 00:00:00 +0000
518+++ _integration-tests/tests/failover_rclocal_crash_test.go 2015-08-02 12:28:52 +0000
519@@ -0,0 +1,54 @@
520+// -*- Mode: Go; indent-tabs-mode: t -*-
521+
522+/*
523+ * Copyright (C) 2015 Canonical Ltd
524+ *
525+ * This program is free software: you can redistribute it and/or modify
526+ * it under the terms of the GNU General Public License version 3 as
527+ * published by the Free Software Foundation.
528+ *
529+ * This program is distributed in the hope that it will be useful,
530+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
531+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
532+ * GNU General Public License for more details.
533+ *
534+ * You should have received a copy of the GNU General Public License
535+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
536+ *
537+ */
538+
539+package tests
540+
541+import (
542+ "fmt"
543+
544+ . "launchpad.net/snappy/_integration-tests/testutils/common"
545+
546+ check "gopkg.in/check.v1"
547+)
548+
549+type rcLocalCrash struct{}
550+
551+func (rcLocalCrash) set(c *check.C) {
552+ MakeWritable(c, BaseAltPartitionPath)
553+ defer MakeReadonly(c, BaseAltPartitionPath)
554+ targetFile := fmt.Sprintf("%s/etc/rc.local", BaseAltPartitionPath)
555+ ExecCommand(c, "sudo", "chmod", "a+xw", targetFile)
556+ ExecCommandToFile(c, targetFile,
557+ "sudo", "echo", "#!bin/sh\nprintf c > /proc/sysrq-trigger")
558+}
559+
560+func (rcLocalCrash) unset(c *check.C) {
561+ MakeWritable(c, BaseAltPartitionPath)
562+ defer MakeReadonly(c, BaseAltPartitionPath)
563+ ExecCommand(c, "sudo", "rm", fmt.Sprintf("%s/etc/rc.local", BaseAltPartitionPath))
564+}
565+
566+/*
567+TODO: uncomment when bug https://bugs.launchpad.net/snappy/+bug/1476129 is fixed
568+(fgimenez 20150728)
569+
570+func (s *failoverSuite) TestRCLocalCrash(c *check.C) {
571+ commonFailoverTest(c, rcLocalCrash{})
572+}
573+*/
574
575=== added file '_integration-tests/tests/failover_systemd_loop_test.go'
576--- _integration-tests/tests/failover_systemd_loop_test.go 1970-01-01 00:00:00 +0000
577+++ _integration-tests/tests/failover_systemd_loop_test.go 2015-08-02 12:28:52 +0000
578@@ -0,0 +1,114 @@
579+// -*- Mode: Go; indent-tabs-mode: t -*-
580+
581+/*
582+ * Copyright (C) 2015 Canonical Ltd
583+ *
584+ * This program is free software: you can redistribute it and/or modify
585+ * it under the terms of the GNU General Public License version 3 as
586+ * published by the Free Software Foundation.
587+ *
588+ * This program is distributed in the hope that it will be useful,
589+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
590+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
591+ * GNU General Public License for more details.
592+ *
593+ * You should have received a copy of the GNU General Public License
594+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
595+ *
596+ */
597+
598+package tests
599+
600+import (
601+ "fmt"
602+
603+ . "launchpad.net/snappy/_integration-tests/testutils/common"
604+
605+ check "gopkg.in/check.v1"
606+)
607+
608+const (
609+ deadlockService = `[Unit]
610+Before=sysinit.target
611+DefaultDependencies=no
612+
613+[Service]
614+Type=oneshot
615+ExecStartPre=-/bin/sh -c "echo 'DEBUG: $(date): deadlocked system' >/dev/console"
616+ExecStartPre=-/bin/sh -c "echo 'DEBUG: $(date): deadlocked system' >/dev/ttyS0"
617+ExecStart=/bin/systemctl start deadlock.service
618+RemainAfterExit=yes
619+
620+[Install]
621+RequiredBy=sysinit.target
622+`
623+ rebootService = `[Unit]
624+DefaultDependencies=no
625+Description=Hack to force reboot if booting did not finish after 20s
626+
627+[Service]
628+Type=oneshot
629+ExecStartPre=/bin/sleep 20
630+ExecStart=-/bin/sh -c 'if ! systemctl is-active default.target; then wall "EMERGENCY REBOOT"; reboot -f; fi'
631+
632+[Install]
633+RequiredBy=sysinit.target
634+`
635+ baseSystemdPath = "/lib/systemd/system"
636+ systemdTargetRequiresDir = "sysinit.target.requires"
637+)
638+
639+type systemdDependencyLoop struct{}
640+
641+func (systemdDependencyLoop) set(c *check.C) {
642+ installService(c, "deadlock", deadlockService, BaseAltPartitionPath)
643+ installService(c, "emerg-reboot", rebootService, BaseAltPartitionPath)
644+}
645+
646+func (systemdDependencyLoop) unset(c *check.C) {
647+ unInstallService(c, "deadlock", BaseAltPartitionPath)
648+ unInstallService(c, "emerg-reboot", BaseAltPartitionPath)
649+}
650+
651+func installService(c *check.C, serviceName, serviceCfg, basePath string) {
652+ MakeWritable(c, basePath)
653+ defer MakeReadonly(c, basePath)
654+
655+ // Create service file
656+ serviceFile := fmt.Sprintf("%s%s/%s.service", basePath, baseSystemdPath, serviceName)
657+ ExecCommand(c, "sudo", "chmod", "a+w", fmt.Sprintf("%s%s", basePath, baseSystemdPath))
658+ ExecCommandToFile(c, serviceFile, "sudo", "echo", serviceCfg)
659+
660+ // Create requires directory
661+ requiresDirPart := fmt.Sprintf("%s/%s", baseSystemdPath, systemdTargetRequiresDir)
662+ requiresDir := fmt.Sprintf("%s%s", basePath, requiresDirPart)
663+ ExecCommand(c, "sudo", "mkdir", "-p", requiresDir)
664+
665+ // Symlink from the requires dir to the service file (with chroot for being
666+ // usable in the other partition)
667+ ExecCommand(c, "sudo", "chroot", basePath, "ln", "-s",
668+ fmt.Sprintf("%s/%s.service", baseSystemdPath, serviceName),
669+ fmt.Sprintf("%s/%s.service", requiresDirPart, serviceName),
670+ )
671+}
672+
673+func unInstallService(c *check.C, serviceName, basePath string) {
674+ MakeWritable(c, basePath)
675+ defer MakeReadonly(c, basePath)
676+
677+ // Disable the service
678+ ExecCommand(c, "sudo", "chroot", basePath,
679+ "systemctl", "disable", fmt.Sprintf("%s.service", serviceName))
680+
681+ // Remove the service file
682+ ExecCommand(c, "sudo", "rm",
683+ fmt.Sprintf("%s%s/%s.service", basePath, baseSystemdPath, serviceName))
684+
685+ // Remove the requires symlink
686+ ExecCommand(c, "sudo", "rm",
687+ fmt.Sprintf("%s%s/%s/%s.service", basePath, baseSystemdPath, systemdTargetRequiresDir, serviceName))
688+}
689+
690+func (s *failoverSuite) TestSystemdDependencyLoop(c *check.C) {
691+ commonFailoverTest(c, systemdDependencyLoop{})
692+}
693
694=== added file '_integration-tests/tests/failover_test.go'
695--- _integration-tests/tests/failover_test.go 1970-01-01 00:00:00 +0000
696+++ _integration-tests/tests/failover_test.go 2015-08-02 12:28:52 +0000
697@@ -0,0 +1,58 @@
698+// -*- Mode: Go; indent-tabs-mode: t -*-
699+
700+/*
701+ * Copyright (C) 2015 Canonical Ltd
702+ *
703+ * This program is free software: you can redistribute it and/or modify
704+ * it under the terms of the GNU General Public License version 3 as
705+ * published by the Free Software Foundation.
706+ *
707+ * This program is distributed in the hope that it will be useful,
708+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
709+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
710+ * GNU General Public License for more details.
711+ *
712+ * You should have received a copy of the GNU General Public License
713+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
714+ *
715+ */
716+
717+package tests
718+
719+import (
720+ check "gopkg.in/check.v1"
721+
722+ . "launchpad.net/snappy/_integration-tests/testutils/common"
723+)
724+
725+var _ = check.Suite(&failoverSuite{})
726+
727+type failoverSuite struct {
728+ SnappySuite
729+}
730+
731+// The types that implement this interface can be used in the test logic
732+type failer interface {
733+ // Sets the failure conditions
734+ set(c *check.C)
735+ // Unsets the failure conditions
736+ unset(c *check.C)
737+}
738+
739+// This is the logic common to all the failover tests. Each of them has define a
740+// type implementing the failer interface and call this function with an instance
741+// of it
742+func commonFailoverTest(c *check.C, f failer) {
743+ currentVersion := GetCurrentUbuntuCoreVersion(c)
744+
745+ if AfterReboot(c) {
746+ RemoveRebootMark(c)
747+ f.unset(c)
748+ c.Assert(GetSavedVersion(c), check.Equals, currentVersion)
749+ } else {
750+ SetSavedVersion(c, currentVersion-1)
751+ CallFakeUpdate(c)
752+ f.set(c)
753+ Reboot(c)
754+ }
755+}
756
757=== added file '_integration-tests/tests/failover_zero_size_file_test.go'
758--- _integration-tests/tests/failover_zero_size_file_test.go 1970-01-01 00:00:00 +0000
759+++ _integration-tests/tests/failover_zero_size_file_test.go 2015-08-02 12:28:52 +0000
760@@ -0,0 +1,258 @@
761+// -*- Mode: Go; indent-tabs-mode: t -*-
762+
763+/*
764+ * Copyright (C) 2015 Canonical Ltd
765+ *
766+ * This program is free software: you can redistribute it and/or modify
767+ * it under the terms of the GNU General Public License version 3 as
768+ * published by the Free Software Foundation.
769+ *
770+ * This program is distributed in the hope that it will be useful,
771+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
772+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
773+ * GNU General Public License for more details.
774+ *
775+ * You should have received a copy of the GNU General Public License
776+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
777+ *
778+ */
779+
780+package tests
781+
782+import (
783+ "bufio"
784+ "fmt"
785+ "os"
786+ "path/filepath"
787+ "strings"
788+
789+ . "launchpad.net/snappy/_integration-tests/testutils/common"
790+
791+ check "gopkg.in/check.v1"
792+)
793+
794+const (
795+ origBootFilenamePattern = "boot/%s%s*"
796+ origSystemdFilenamePattern = "lib/systemd/%s%s"
797+ kernelFilename = "vmlinuz"
798+ initrdFilename = "initrd"
799+ systemdFilename = "systemd"
800+ destFilenamePrefix = "snappy-selftest-"
801+ bootBase = "/boot"
802+ ubootDir = bootBase + "/uboot"
803+ grubDir = bootBase + "/grub"
804+ ubootConfigFile = ubootDir + "/snappy-system.txt"
805+ grubConfigFile = grubDir + "/grubenv"
806+)
807+
808+type zeroSizeKernel struct{}
809+type zeroSizeInitrd struct{}
810+type zeroSizeSystemd struct{}
811+
812+func (zeroSizeKernel) set(c *check.C) {
813+ commonSet(c, BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
814+}
815+
816+func (zeroSizeKernel) unset(c *check.C) {
817+ commonUnset(c, BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
818+}
819+
820+func (zeroSizeInitrd) set(c *check.C) {
821+ if classicKernelFiles(c) {
822+ commonSet(c, BaseAltPartitionPath, origBootFilenamePattern, initrdFilename)
823+ } else {
824+ boot := bootSystem(c)
825+ dir := bootDirectory(boot)
826+ bootFileNamePattern := newKernelFilenamePattern(c, boot, true)
827+ commonSet(c, dir, bootFileNamePattern, initrdFilename)
828+ }
829+}
830+
831+func (zeroSizeInitrd) unset(c *check.C) {
832+ if classicKernelFiles(c) {
833+ commonUnset(c, BaseAltPartitionPath, origBootFilenamePattern, initrdFilename)
834+ } else {
835+ boot := bootSystem(c)
836+ dir := bootDirectory(boot)
837+ bootFileNamePattern := newKernelFilenamePattern(c, boot, false)
838+ commonUnset(c, dir, bootFileNamePattern, initrdFilename)
839+ }
840+}
841+
842+func (zeroSizeSystemd) set(c *check.C) {
843+ commonSet(c, BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
844+}
845+
846+func (zeroSizeSystemd) unset(c *check.C) {
847+ commonUnset(c, BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
848+}
849+
850+func commonSet(c *check.C, baseOtherPath, origPattern, filename string) {
851+ filenamePattern := fmt.Sprintf(origPattern, "", filename)
852+ completePattern := filepath.Join(
853+ baseOtherPath,
854+ filenamePattern)
855+ oldFilename := getSingleFilename(c, completePattern)
856+ filenameSuffix := fmt.Sprintf(
857+ strings.Replace(origPattern, "*", "", 1), destFilenamePrefix, filepath.Base(oldFilename))
858+ newFilename := fmt.Sprintf(
859+ "%s/%s", baseOtherPath, filenameSuffix)
860+
861+ renameFile(c, baseOtherPath, oldFilename, newFilename, true)
862+}
863+
864+func commonUnset(c *check.C, baseOtherPath, origPattern, filename string) {
865+ completePattern := filepath.Join(
866+ baseOtherPath,
867+ fmt.Sprintf(origPattern, destFilenamePrefix, filename))
868+ oldFilename := getSingleFilename(c, completePattern)
869+ newFilename := strings.Replace(oldFilename, destFilenamePrefix, "", 1)
870+
871+ renameFile(c, baseOtherPath, oldFilename, newFilename, false)
872+}
873+
874+func renameFile(c *check.C, basePath, oldFilename, newFilename string, keepOld bool) {
875+ // Only need to make writable and revert for BaseAltPartitionPath,
876+ // kernel files' boot directory is writable
877+ if basePath == BaseAltPartitionPath {
878+ MakeWritable(c, basePath)
879+ defer MakeReadonly(c, basePath)
880+ }
881+
882+ ExecCommand(c, "sudo", "mv", oldFilename, newFilename)
883+
884+ if keepOld {
885+ ExecCommand(c, "sudo", "touch", oldFilename)
886+ mode := getFileMode(c, newFilename)
887+ ExecCommand(c, "sudo", "chmod", fmt.Sprintf("%o", mode), oldFilename)
888+ }
889+}
890+
891+func getFileMode(c *check.C, filePath string) os.FileMode {
892+ info, err := os.Stat(filePath)
893+ c.Check(err, check.IsNil, check.Commentf("Error getting Stat of %s", filePath))
894+
895+ return info.Mode()
896+}
897+
898+func getSingleFilename(c *check.C, pattern string) string {
899+ matches, err := filepath.Glob(pattern)
900+
901+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", err))
902+ c.Assert(len(matches), check.Equals, 1,
903+ check.Commentf("%d files matching %s, 1 expected", len(matches), pattern))
904+
905+ return matches[0]
906+}
907+
908+func classicKernelFiles(c *check.C) bool {
909+ initrdClassicFilenamePattern := fmt.Sprintf("/boot/%s*-generic", initrdFilename)
910+ matches, err := filepath.Glob(initrdClassicFilenamePattern)
911+
912+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", err))
913+
914+ return len(matches) == 1
915+}
916+
917+func bootSystem(c *check.C) string {
918+ matches, err := filepath.Glob(bootBase + "/grub")
919+
920+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", err))
921+
922+ if len(matches) == 1 {
923+ return "grub"
924+ }
925+ return "uboot"
926+}
927+
928+func bootDirectory(bootSystem string) string {
929+ if bootSystem == "grub" {
930+ return grubDir
931+ }
932+ return ubootDir
933+}
934+
935+func bootConfigFile(bootSystem string) string {
936+ if bootSystem == "grub" {
937+ return grubConfigFile
938+ }
939+ return ubootConfigFile
940+}
941+
942+func currentPartition(c *check.C, bootSystem string) (partition string) {
943+ bootConfigFile := bootConfigFile(bootSystem)
944+ file, err := os.Open(bootConfigFile)
945+
946+ c.Assert(err, check.IsNil,
947+ check.Commentf("Error reading boot config file %s", bootConfigFile))
948+
949+ defer file.Close()
950+
951+ reader := bufio.NewReader(file)
952+ scanner := bufio.NewScanner(reader)
953+
954+ scanner.Split(bufio.ScanLines)
955+
956+ for scanner.Scan() {
957+ if strings.HasPrefix(scanner.Text(), "snappy_ab") {
958+ fields := strings.Split(scanner.Text(), "=")
959+ if len(fields) > 1 {
960+ if bootSystem == "grub" {
961+ partition = fields[1]
962+ } else {
963+ partition = otherPart(fields[1])
964+ }
965+ }
966+ return
967+ }
968+ }
969+ return
970+}
971+
972+func otherPart(current string) string {
973+ if current == "a" {
974+ return "b"
975+ }
976+ return "a"
977+}
978+
979+// newKernelFilenamePattern returns the filename pattern to modify files
980+// in the partition declared in the boot config file.
981+//
982+// After the update, the config file is already changed to point to the new partition.
983+// If we are on a and update, the config file would point to b
984+// and this function would return "b/%s%s*"
985+// If we are not in an update process (ie. we are unsetting the failover conditions)
986+// we want to change the files in the other partition
987+func newKernelFilenamePattern(c *check.C, bootSystem string, afterUpdate bool) string {
988+ var actualPartition string
989+ partition := currentPartition(c, bootSystem)
990+ if afterUpdate {
991+ actualPartition = partition
992+ } else {
993+ actualPartition = otherPart(partition)
994+ }
995+ return filepath.Join(actualPartition, "%s%s*")
996+}
997+
998+/*
999+TODO: uncomment when bug https://bugs.launchpad.net/snappy/+bug/1467553 is fixed
1000+(fgimenez 20150729)
1001+
1002+func (s *failoverSuite) TestZeroSizeKernel(c *check.C) {
1003+ commonFailoverTest(c, zeroSizeKernel{})
1004+}
1005+*/
1006+
1007+func (s *failoverSuite) TestZeroSizeInitrd(c *check.C) {
1008+ // Skip if on uboot due to https://bugs.launchpad.net/snappy/+bug/1480248
1009+ // (fgimenez 20150731)
1010+ if bootSystem(c) == "uboot" {
1011+ c.Skip("Failover for empty initrd not working in uboot")
1012+ }
1013+ commonFailoverTest(c, zeroSizeInitrd{})
1014+}
1015+
1016+func (s *failoverSuite) TestZeroSizeSystemd(c *check.C) {
1017+ commonFailoverTest(c, zeroSizeSystemd{})
1018+}
1019
1020=== added file '_integration-tests/tests/info_test.go'
1021--- _integration-tests/tests/info_test.go 1970-01-01 00:00:00 +0000
1022+++ _integration-tests/tests/info_test.go 2015-08-02 12:28:52 +0000
1023@@ -0,0 +1,79 @@
1024+// -*- Mode: Go; indent-tabs-mode: t -*-
1025+
1026+/*
1027+ * Copyright (C) 2015 Canonical Ltd
1028+ *
1029+ * This program is free software: you can redistribute it and/or modify
1030+ * it under the terms of the GNU General Public License version 3 as
1031+ * published by the Free Software Foundation.
1032+ *
1033+ * This program is distributed in the hope that it will be useful,
1034+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1035+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1036+ * GNU General Public License for more details.
1037+ *
1038+ * You should have received a copy of the GNU General Public License
1039+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1040+ *
1041+ */
1042+
1043+package tests
1044+
1045+import (
1046+ "fmt"
1047+
1048+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1049+
1050+ check "gopkg.in/check.v1"
1051+)
1052+
1053+var _ = check.Suite(&infoSuite{})
1054+
1055+type infoSuite struct {
1056+ SnappySuite
1057+}
1058+
1059+func (s *infoSuite) TestInfoMustPrintReleaseAndChannel(c *check.C) {
1060+ // skip test when having a remote testbed (we can't know which the
1061+ // release and channels are)
1062+ if Cfg.RemoteTestbed {
1063+ c.Skip(fmt.Sprintf(
1064+ "Skipping %s while testing in remote testbed",
1065+ c.TestName()))
1066+ }
1067+
1068+ infoOutput := ExecCommand(c, "snappy", "info")
1069+
1070+ expected := "(?ms)" +
1071+ fmt.Sprintf("^release: ubuntu-core/%s/%s\n", Cfg.Release, Cfg.Channel) +
1072+ ".*"
1073+
1074+ c.Assert(infoOutput, check.Matches, expected)
1075+}
1076+
1077+func (s *infoSuite) TestInfoMustPrintInstalledApps(c *check.C) {
1078+ InstallSnap(c, "hello-world")
1079+ s.AddCleanup(func() {
1080+ RemoveSnap(c, "hello-world")
1081+ })
1082+ infoOutput := ExecCommand(c, "snappy", "info")
1083+
1084+ expected := "(?ms)" +
1085+ ".*" +
1086+ "^apps: .*hello-world.*\n"
1087+ c.Assert(infoOutput, check.Matches, expected)
1088+}
1089+
1090+func (s *infoSuite) TestInfoMustPrintInstalledFrameworks(c *check.C) {
1091+ InstallSnap(c, "hello-dbus-fwk.canonical")
1092+ s.AddCleanup(func() {
1093+ RemoveSnap(c, "hello-dbus-fwk.canonical")
1094+ })
1095+ infoOutput := ExecCommand(c, "snappy", "info")
1096+
1097+ expected := "(?ms)" +
1098+ ".*" +
1099+ "^frameworks: .*hello-dbus-fwk.*\n" +
1100+ ".*"
1101+ c.Assert(infoOutput, check.Matches, expected)
1102+}
1103
1104=== added file '_integration-tests/tests/installApp_test.go'
1105--- _integration-tests/tests/installApp_test.go 1970-01-01 00:00:00 +0000
1106+++ _integration-tests/tests/installApp_test.go 2015-08-02 12:28:52 +0000
1107@@ -0,0 +1,128 @@
1108+// -*- Mode: Go; indent-tabs-mode: t -*-
1109+
1110+/*
1111+ * Copyright (C) 2015 Canonical Ltd
1112+ *
1113+ * This program is free software: you can redistribute it and/or modify
1114+ * it under the terms of the GNU General Public License version 3 as
1115+ * published by the Free Software Foundation.
1116+ *
1117+ * This program is distributed in the hope that it will be useful,
1118+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1119+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1120+ * GNU General Public License for more details.
1121+ *
1122+ * You should have received a copy of the GNU General Public License
1123+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1124+ *
1125+ */
1126+
1127+package tests
1128+
1129+import (
1130+ "fmt"
1131+ "net/http"
1132+ "os/exec"
1133+ "time"
1134+
1135+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1136+
1137+ check "gopkg.in/check.v1"
1138+)
1139+
1140+var _ = check.Suite(&installAppSuite{})
1141+
1142+type installAppSuite struct {
1143+ SnappySuite
1144+}
1145+
1146+func (s *installAppSuite) TestInstallAppMustPrintPackageInformation(c *check.C) {
1147+ installOutput := InstallSnap(c, "hello-world")
1148+ s.AddCleanup(func() {
1149+ RemoveSnap(c, "hello-world")
1150+ })
1151+
1152+ expected := "(?ms)" +
1153+ "Installing hello-world\n" +
1154+ "Name +Date +Version +Developer \n" +
1155+ ".*" +
1156+ "^hello-world +.* +.* +canonical \n" +
1157+ ".*"
1158+
1159+ c.Assert(installOutput, check.Matches, expected)
1160+}
1161+
1162+func (s *installAppSuite) TestCallBinaryFromInstalledSnap(c *check.C) {
1163+ InstallSnap(c, "hello-world")
1164+ s.AddCleanup(func() {
1165+ RemoveSnap(c, "hello-world")
1166+ })
1167+
1168+ echoOutput := ExecCommand(c, "hello-world.echo")
1169+
1170+ c.Assert(echoOutput, check.Equals, "Hello World!\n")
1171+}
1172+
1173+func (s *installAppSuite) TestCallBinaryWithPermissionDeniedMustPrintError(c *check.C) {
1174+ InstallSnap(c, "hello-world")
1175+ s.AddCleanup(func() {
1176+ RemoveSnap(c, "hello-world")
1177+ })
1178+
1179+ cmd := exec.Command("hello-world.evil")
1180+ echoOutput, err := cmd.CombinedOutput()
1181+ c.Assert(err, check.NotNil, check.Commentf("hello-world.evil did not fail"))
1182+
1183+ expected := "" +
1184+ "Hello Evil World!\n" +
1185+ "This example demonstrates the app confinement\n" +
1186+ "You should see a permission denied error next\n" +
1187+ "/apps/hello-world.canonical/.*/bin/evil: \\d+: " +
1188+ "/apps/hello-world.canonical/.*/bin/evil: " +
1189+ "cannot create /var/tmp/myevil.txt: Permission denied\n"
1190+
1191+ c.Assert(string(echoOutput), check.Matches, expected)
1192+}
1193+
1194+func (s *installAppSuite) TestInfoMustPrintInstalledPackageInformation(c *check.C) {
1195+ InstallSnap(c, "hello-world")
1196+ s.AddCleanup(func() {
1197+ RemoveSnap(c, "hello-world")
1198+ })
1199+
1200+ infoOutput := ExecCommand(c, "snappy", "info")
1201+
1202+ expected := "(?ms).*^apps: hello-world\n"
1203+ c.Assert(infoOutput, check.Matches, expected)
1204+}
1205+
1206+func (s *installAppSuite) TestAppNetworkingServiceMustBeStarted(c *check.C) {
1207+ baseAppName := "xkcd-webserver"
1208+ appName := baseAppName + ".canonical"
1209+ InstallSnap(c, appName)
1210+ s.AddCleanup(func() {
1211+ RemoveSnap(c, appName)
1212+ })
1213+
1214+ appVersion := GetCurrentVersion(c, baseAppName)
1215+ appService := fmt.Sprintf("%s_%s_%s.service", baseAppName, baseAppName, appVersion)
1216+
1217+ err := WaitForActiveService(c, appService)
1218+ c.Assert(err, check.IsNil)
1219+
1220+ time.Sleep(1 * time.Second)
1221+ resp, err := http.Get("http://localhost")
1222+ c.Assert(err, check.IsNil)
1223+ c.Check(resp.Status, check.Equals, "200 OK")
1224+ c.Assert(resp.Proto, check.Equals, "HTTP/1.0")
1225+}
1226+
1227+func (s *installAppSuite) TestInstallUnexistingAppMustPrintError(c *check.C) {
1228+ cmd := exec.Command("sudo", "snappy", "install", "unexisting.canonical")
1229+ output, err := cmd.CombinedOutput()
1230+
1231+ c.Assert(err, check.NotNil)
1232+ c.Assert(string(output), check.Equals,
1233+ "Installing unexisting.canonical\n"+
1234+ "unexisting.canonical failed to install: snappy package not found\n")
1235+}
1236
1237=== added file '_integration-tests/tests/installFramework_test.go'
1238--- _integration-tests/tests/installFramework_test.go 1970-01-01 00:00:00 +0000
1239+++ _integration-tests/tests/installFramework_test.go 2015-08-02 12:28:52 +0000
1240@@ -0,0 +1,93 @@
1241+// -*- Mode: Go; indent-tabs-mode: t -*-
1242+
1243+/*
1244+ * Copyright (C) 2015 Canonical Ltd
1245+ *
1246+ * This program is free software: you can redistribute it and/or modify
1247+ * it under the terms of the GNU General Public License version 3 as
1248+ * published by the Free Software Foundation.
1249+ *
1250+ * This program is distributed in the hope that it will be useful,
1251+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1252+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1253+ * GNU General Public License for more details.
1254+ *
1255+ * You should have received a copy of the GNU General Public License
1256+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1257+ *
1258+ */
1259+
1260+package tests
1261+
1262+import (
1263+ "fmt"
1264+ "regexp"
1265+
1266+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1267+
1268+ check "gopkg.in/check.v1"
1269+)
1270+
1271+var _ = check.Suite(&installFrameworkSuite{})
1272+
1273+type installFrameworkSuite struct {
1274+ SnappySuite
1275+}
1276+
1277+func (s *installFrameworkSuite) TearDownTest(c *check.C) {
1278+ if !NeedsReboot() && CheckRebootMark("") {
1279+ RemoveSnap(c, "docker")
1280+ }
1281+ // run cleanup last
1282+ s.SnappySuite.TearDownTest(c)
1283+}
1284+
1285+func isDockerServiceRunning(c *check.C) bool {
1286+ dockerVersion := GetCurrentVersion(c, "docker")
1287+ dockerService := fmt.Sprintf("docker_docker-daemon_%s.service", dockerVersion)
1288+
1289+ err := WaitForActiveService(c, dockerService)
1290+ c.Assert(err, check.IsNil)
1291+
1292+ statusOutput := ExecCommand(
1293+ c, "systemctl", "status",
1294+ dockerService)
1295+
1296+ expected := "(?ms)" +
1297+ ".* docker_docker-daemon_.*\\.service .*\n" +
1298+ ".*Loaded: loaded .*\n" +
1299+ ".*Active: active \\(running\\) .*\n" +
1300+ ".*"
1301+
1302+ matched, err := regexp.MatchString(expected, statusOutput)
1303+ c.Assert(err, check.IsNil)
1304+ return matched
1305+}
1306+
1307+func (s *installFrameworkSuite) TestInstallFrameworkMustPrintPackageInformation(c *check.C) {
1308+ installOutput := InstallSnap(c, "docker")
1309+
1310+ expected := "(?ms)" +
1311+ "Installing docker\n" +
1312+ "Name +Date +Version +Developer \n" +
1313+ ".*" +
1314+ "^docker +.* +.* +canonical \n" +
1315+ ".*"
1316+
1317+ c.Assert(installOutput, check.Matches, expected)
1318+}
1319+
1320+func (s *installFrameworkSuite) TestInstalledFrameworkServiceMustBeStarted(c *check.C) {
1321+ InstallSnap(c, "docker")
1322+ c.Assert(isDockerServiceRunning(c), check.Equals, true)
1323+}
1324+
1325+func (s *installFrameworkSuite) TestFrameworkServiceMustBeStartedAfterReboot(c *check.C) {
1326+ if BeforeReboot() {
1327+ InstallSnap(c, "docker")
1328+ Reboot(c)
1329+ } else if AfterReboot(c) {
1330+ RemoveRebootMark(c)
1331+ c.Assert(isDockerServiceRunning(c), check.Equals, true)
1332+ }
1333+}
1334
1335=== added file '_integration-tests/tests/list_test.go'
1336--- _integration-tests/tests/list_test.go 1970-01-01 00:00:00 +0000
1337+++ _integration-tests/tests/list_test.go 2015-08-02 12:28:52 +0000
1338@@ -0,0 +1,78 @@
1339+// -*- Mode: Go; indent-tabs-mode: t -*-
1340+
1341+/*
1342+ * Copyright (C) 2015 Canonical Ltd
1343+ *
1344+ * This program is free software: you can redistribute it and/or modify
1345+ * it under the terms of the GNU General Public License version 3 as
1346+ * published by the Free Software Foundation.
1347+ *
1348+ * This program is distributed in the hope that it will be useful,
1349+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1350+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1351+ * GNU General Public License for more details.
1352+ *
1353+ * You should have received a copy of the GNU General Public License
1354+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1355+ *
1356+ */
1357+
1358+package tests
1359+
1360+import (
1361+ "fmt"
1362+ "os"
1363+
1364+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1365+
1366+ "github.com/mvo5/goconfigparser"
1367+ check "gopkg.in/check.v1"
1368+)
1369+
1370+var _ = check.Suite(&listSuite{})
1371+
1372+type listSuite struct {
1373+ SnappySuite
1374+}
1375+
1376+func getVersionFromConfig(c *check.C) string {
1377+ cfg := goconfigparser.New()
1378+ f, err := os.Open("/etc/system-image/channel.ini")
1379+ c.Assert(err, check.IsNil,
1380+ check.Commentf("Error opening the config file: %v:", err))
1381+ defer f.Close()
1382+ err = cfg.Read(f)
1383+ c.Assert(err, check.IsNil,
1384+ check.Commentf("Error parsing the config file: %v", err))
1385+ version, err := cfg.Get("service", "build_number")
1386+ c.Assert(err, check.IsNil,
1387+ check.Commentf("Error getting the build number: %v", err))
1388+ return version
1389+}
1390+
1391+func (s *listSuite) TestListMustPrintCoreVersion(c *check.C) {
1392+ listOutput := ExecCommand(c, "snappy", "list")
1393+
1394+ expected := "(?ms)" +
1395+ "Name +Date +Version +Developer *\n" +
1396+ ".*" +
1397+ fmt.Sprintf("^ubuntu-core +.* +%s +ubuntu *\n", getVersionFromConfig(c)) +
1398+ ".*"
1399+ c.Assert(listOutput, check.Matches, expected)
1400+}
1401+
1402+func (s *listSuite) TestListMustPrintAppVersion(c *check.C) {
1403+ InstallSnap(c, "hello-world")
1404+ s.AddCleanup(func() {
1405+ RemoveSnap(c, "hello-world")
1406+ })
1407+
1408+ listOutput := ExecCommand(c, "snappy", "list")
1409+ expected := "(?ms)" +
1410+ "Name +Date +Version +Developer *\n" +
1411+ ".*" +
1412+ "^hello-world +.* +(\\d+)(\\.\\d+)* +.* +.* *\n" +
1413+ ".*"
1414+
1415+ c.Assert(listOutput, check.Matches, expected)
1416+}
1417
1418=== added file '_integration-tests/tests/rollback_test.go'
1419--- _integration-tests/tests/rollback_test.go 1970-01-01 00:00:00 +0000
1420+++ _integration-tests/tests/rollback_test.go 2015-08-02 12:28:52 +0000
1421@@ -0,0 +1,53 @@
1422+// -*- Mode: Go; indent-tabs-mode: t -*-
1423+
1424+/*
1425+ * Copyright (C) 2015 Canonical Ltd
1426+ *
1427+ * This program is free software: you can redistribute it and/or modify
1428+ * it under the terms of the GNU General Public License version 3 as
1429+ * published by the Free Software Foundation.
1430+ *
1431+ * This program is distributed in the hope that it will be useful,
1432+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1433+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1434+ * GNU General Public License for more details.
1435+ *
1436+ * You should have received a copy of the GNU General Public License
1437+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1438+ *
1439+ */
1440+
1441+package tests
1442+
1443+import (
1444+ "strconv"
1445+
1446+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1447+
1448+ check "gopkg.in/check.v1"
1449+)
1450+
1451+var _ = check.Suite(&rollbackSuite{})
1452+
1453+type rollbackSuite struct {
1454+ SnappySuite
1455+}
1456+
1457+func (s *rollbackSuite) TestRollbackMustRebootToOtherVersion(c *check.C) {
1458+ if BeforeReboot() {
1459+ CallFakeUpdate(c)
1460+ Reboot(c)
1461+ } else if CheckRebootMark(c.TestName()) {
1462+ RemoveRebootMark(c)
1463+ currentVersion := GetCurrentUbuntuCoreVersion(c)
1464+ c.Assert(currentVersion > GetSavedVersion(c), check.Equals, true)
1465+ ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core",
1466+ strconv.Itoa(GetSavedVersion(c)))
1467+ SetSavedVersion(c, currentVersion)
1468+ RebootWithMark(c, c.TestName()+"-rollback")
1469+ } else if CheckRebootMark(c.TestName() + "-rollback") {
1470+ RemoveRebootMark(c)
1471+ c.Assert(
1472+ GetCurrentUbuntuCoreVersion(c) < GetSavedVersion(c), check.Equals, true)
1473+ }
1474+}
1475
1476=== added file '_integration-tests/tests/search_test.go'
1477--- _integration-tests/tests/search_test.go 1970-01-01 00:00:00 +0000
1478+++ _integration-tests/tests/search_test.go 2015-08-02 12:28:52 +0000
1479@@ -0,0 +1,44 @@
1480+// -*- Mode: Go; indent-tabs-mode: t -*-
1481+
1482+/*
1483+ * Copyright (C) 2015 Canonical Ltd
1484+ *
1485+ * This program is free software: you can redistribute it and/or modify
1486+ * it under the terms of the GNU General Public License version 3 as
1487+ * published by the Free Software Foundation.
1488+ *
1489+ * This program is distributed in the hope that it will be useful,
1490+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1491+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1492+ * GNU General Public License for more details.
1493+ *
1494+ * You should have received a copy of the GNU General Public License
1495+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1496+ *
1497+ */
1498+
1499+package tests
1500+
1501+import (
1502+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1503+
1504+ . "gopkg.in/check.v1"
1505+)
1506+
1507+var _ = Suite(&searchSuite{})
1508+
1509+type searchSuite struct {
1510+ SnappySuite
1511+}
1512+
1513+func (s *searchSuite) TestSearchFrameworkMustPrintMatch(c *C) {
1514+ searchOutput := ExecCommand(c, "snappy", "search", "hello-dbus-fwk")
1515+
1516+ expected := "(?ms)" +
1517+ "Name +Version +Summary *\n" +
1518+ ".*" +
1519+ "^hello-dbus-fwk +.* +hello-dbus-fwk *\n" +
1520+ ".*"
1521+
1522+ c.Assert(searchOutput, Matches, expected)
1523+}
1524
1525=== added file '_integration-tests/tests/update_test.go'
1526--- _integration-tests/tests/update_test.go 1970-01-01 00:00:00 +0000
1527+++ _integration-tests/tests/update_test.go 2015-08-02 12:28:52 +0000
1528@@ -0,0 +1,51 @@
1529+// -*- Mode: Go; indent-tabs-mode: t -*-
1530+
1531+/*
1532+ * Copyright (C) 2015 Canonical Ltd
1533+ *
1534+ * This program is free software: you can redistribute it and/or modify
1535+ * it under the terms of the GNU General Public License version 3 as
1536+ * published by the Free Software Foundation.
1537+ *
1538+ * This program is distributed in the hope that it will be useful,
1539+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1540+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1541+ * GNU General Public License for more details.
1542+ *
1543+ * You should have received a copy of the GNU General Public License
1544+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1545+ *
1546+ */
1547+
1548+package tests
1549+
1550+import (
1551+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1552+
1553+ check "gopkg.in/check.v1"
1554+)
1555+
1556+var _ = check.Suite(&updateSuite{})
1557+
1558+type updateSuite struct {
1559+ SnappySuite
1560+}
1561+
1562+// Test that the update to the same release and channel must install a newer
1563+// version. If there is no update available, the channel version will be
1564+// modified to fake an update. If there is a version available, the image will
1565+// be up-to-date after running this test.
1566+func (s *updateSuite) TestUpdateToSameReleaseAndChannel(c *check.C) {
1567+ if BeforeReboot() {
1568+ updateOutput := CallFakeUpdate(c)
1569+ expected := "(?ms)" +
1570+ ".*" +
1571+ "^Reboot to use .*ubuntu-core.\n"
1572+ c.Assert(updateOutput, check.Matches, expected)
1573+ Reboot(c)
1574+ } else if AfterReboot(c) {
1575+ RemoveRebootMark(c)
1576+ c.Assert(GetCurrentUbuntuCoreVersion(c) > GetSavedVersion(c),
1577+ check.Equals, true)
1578+ }
1579+}
1580
1581=== added file '_integration-tests/tests/writablePaths_test.go'
1582--- _integration-tests/tests/writablePaths_test.go 1970-01-01 00:00:00 +0000
1583+++ _integration-tests/tests/writablePaths_test.go 2015-08-02 12:28:52 +0000
1584@@ -0,0 +1,106 @@
1585+// -*- Mode: Go; indent-tabs-mode: t -*-
1586+
1587+/*
1588+ * Copyright (C) 2015 Canonical Ltd
1589+ *
1590+ * This program is free software: you can redistribute it and/or modify
1591+ * it under the terms of the GNU General Public License version 3 as
1592+ * published by the Free Software Foundation.
1593+ *
1594+ * This program is distributed in the hope that it will be useful,
1595+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1596+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1597+ * GNU General Public License for more details.
1598+ *
1599+ * You should have received a copy of the GNU General Public License
1600+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1601+ *
1602+ */
1603+
1604+package tests
1605+
1606+import (
1607+ "bufio"
1608+ "fmt"
1609+ "os"
1610+ "os/exec"
1611+ "path/filepath"
1612+ "strings"
1613+
1614+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1615+
1616+ check "gopkg.in/check.v1"
1617+)
1618+
1619+const writablePathsListFile = "/etc/system-image/writable-paths"
1620+
1621+var _ = check.Suite(&writablePathsSuite{})
1622+
1623+type writablePathsSuite struct {
1624+ SnappySuite
1625+}
1626+
1627+var IsWritable check.Checker = &isWritable{}
1628+
1629+type isWritable struct {
1630+}
1631+
1632+func (is *isWritable) Info() *check.CheckerInfo {
1633+ return &check.CheckerInfo{Name: "IsWritable", Params: []string{"path"}}
1634+}
1635+
1636+func (is *isWritable) Check(params []interface{}, names []string) (result bool, error string) {
1637+ if path, ok := params[0].(string); ok {
1638+ filename := filepath.Join(path, "tmpfile")
1639+
1640+ cmd := exec.Command("sudo", "touch", filename)
1641+ rmCmd := exec.Command("sudo", "rm", filename)
1642+ defer rmCmd.Run()
1643+
1644+ if _, err := cmd.CombinedOutput(); err == nil {
1645+ result = true
1646+ } else {
1647+ error = fmt.Sprintf("Error creating file %s", filename)
1648+ }
1649+ } else {
1650+ error = fmt.Sprintf("First param of checker %v is of type %T and it should be a string", params[0], params[0])
1651+ }
1652+ return result, error
1653+}
1654+
1655+func (s *writablePathsSuite) TestWritablePathsAreWritable(c *check.C) {
1656+ for writablePath := range generateWritablePaths(c) {
1657+ c.Logf("Checking if %s is writable", writablePath)
1658+ c.Check(writablePath, IsWritable)
1659+ }
1660+}
1661+
1662+func generateWritablePaths(c *check.C) chan string {
1663+ ch := make(chan string)
1664+
1665+ go func() {
1666+ file, err := os.Open(writablePathsListFile)
1667+
1668+ c.Assert(err, check.IsNil,
1669+ check.Commentf("Error reading writable files list %s", writablePathsListFile))
1670+
1671+ defer file.Close()
1672+
1673+ reader := bufio.NewReader(file)
1674+ scanner := bufio.NewScanner(reader)
1675+
1676+ scanner.Split(bufio.ScanLines)
1677+
1678+ for scanner.Scan() {
1679+ fields := strings.Fields(scanner.Text())
1680+ if len(fields) > 0 && fields[0] != "#" {
1681+ if src, err := os.Stat(fields[0]); err == nil && src.IsDir() {
1682+ ch <- fields[0]
1683+ }
1684+ }
1685+ }
1686+ close(ch)
1687+ }()
1688+
1689+ return ch
1690+}
1691
1692=== added directory '_integration-tests/testutils'
1693=== added directory '_integration-tests/testutils/autopkgtest'
1694=== added file '_integration-tests/testutils/autopkgtest/autopkgtest.go'
1695--- _integration-tests/testutils/autopkgtest/autopkgtest.go 1970-01-01 00:00:00 +0000
1696+++ _integration-tests/testutils/autopkgtest/autopkgtest.go 2015-08-02 12:28:52 +0000
1697@@ -0,0 +1,111 @@
1698+// -*- Mode: Go; indent-tabs-mode: t -*-
1699+
1700+/*
1701+ * Copyright (C) 2015 Canonical Ltd
1702+ *
1703+ * This program is free software: you can redistribute it and/or modify
1704+ * it under the terms of the GNU General Public License version 3 as
1705+ * published by the Free Software Foundation.
1706+ *
1707+ * This program is distributed in the hope that it will be useful,
1708+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1709+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1710+ * GNU General Public License for more details.
1711+ *
1712+ * You should have received a copy of the GNU General Public License
1713+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1714+ *
1715+ */
1716+
1717+package autopkgtest
1718+
1719+import (
1720+ "fmt"
1721+ "os"
1722+ "path/filepath"
1723+ "strconv"
1724+ "text/template"
1725+
1726+ "log"
1727+
1728+ "launchpad.net/snappy/_integration-tests/testutils"
1729+)
1730+
1731+const (
1732+ controlTpl = "_integration-tests/data/tpl/control"
1733+ dataOutputDir = "_integration-tests/data/output/"
1734+)
1735+
1736+var controlFile = filepath.Join(dataOutputDir, "control")
1737+
1738+// Autopkgtest is the type that knows how to call adt-run
1739+type Autopkgtest struct {
1740+ sourceCodePath string // location of the source code on the host
1741+ testArtifactsPath string // location of the test artifacts on the host
1742+ testFilter string
1743+ integrationTestName string
1744+}
1745+
1746+// NewAutopkgtest is the Autopkgtest constructor
1747+func NewAutopkgtest(sourceCodePath, testArtifactsPath, testFilter, integrationTestName string) *Autopkgtest {
1748+ return &Autopkgtest{
1749+ sourceCodePath: sourceCodePath,
1750+ testArtifactsPath: testArtifactsPath,
1751+ testFilter: testFilter,
1752+ integrationTestName: integrationTestName}
1753+}
1754+
1755+// AdtRunLocal starts a kvm running the image passed as argument and runs the
1756+// autopkgtests using it as the testbed.
1757+func (a *Autopkgtest) AdtRunLocal(imgPath string) {
1758+ // Run the tests on the latest rolling edge image.
1759+ a.adtRun(kvmSSHOptions(imgPath))
1760+}
1761+
1762+// AdtRunRemote runs the autopkgtests using a remote machine as the testbed.
1763+func (a *Autopkgtest) AdtRunRemote(testbedIP string, testbedPort int) {
1764+ testutils.ExecCommand("ssh-copy-id", "-p", strconv.Itoa(testbedPort),
1765+ "ubuntu@"+testbedIP)
1766+ a.adtRun(remoteTestbedSSHOptions(testbedIP, testbedPort))
1767+}
1768+
1769+func (a *Autopkgtest) adtRun(testbedOptions []string) {
1770+ a.createControlFile()
1771+
1772+ fmt.Println("Calling adt-run...")
1773+ outputDir := filepath.Join(a.testArtifactsPath, "output")
1774+ testutils.PrepareTargetDir(outputDir)
1775+
1776+ cmd := []string{
1777+ "adt-run", "-B",
1778+ "--setup-commands", "touch /run/autopkgtest_no_reboot.stamp",
1779+ "--override-control", controlFile,
1780+ "--built-tree", a.sourceCodePath,
1781+ "--output-dir", outputDir}
1782+
1783+ testutils.ExecCommand(append(cmd, testbedOptions...)...)
1784+}
1785+
1786+func (a *Autopkgtest) createControlFile() {
1787+ type controlData struct {
1788+ Filter string
1789+ Test string
1790+ }
1791+
1792+ tpl, err := template.ParseFiles(controlTpl)
1793+ if err != nil {
1794+ log.Panicf("Error reading adt-run control template %s", controlTpl)
1795+ }
1796+
1797+ outputFile, err := os.Create(controlFile)
1798+ if err != nil {
1799+ log.Panicf("Error creating control file %s", controlFile)
1800+ }
1801+ defer outputFile.Close()
1802+
1803+ err = tpl.Execute(outputFile,
1804+ controlData{Test: a.integrationTestName, Filter: a.testFilter})
1805+ if err != nil {
1806+ log.Panicf("execution: %s", err)
1807+ }
1808+}
1809
1810=== added file '_integration-tests/testutils/autopkgtest/ssh.go'
1811--- _integration-tests/testutils/autopkgtest/ssh.go 1970-01-01 00:00:00 +0000
1812+++ _integration-tests/testutils/autopkgtest/ssh.go 2015-08-02 12:28:52 +0000
1813@@ -0,0 +1,46 @@
1814+// -*- Mode: Go; indent-tabs-mode: t -*-
1815+
1816+/*
1817+ * Copyright (C) 2015 Canonical Ltd
1818+ *
1819+ * This program is free software: you can redistribute it and/or modify
1820+ * it under the terms of the GNU General Public License version 3 as
1821+ * published by the Free Software Foundation.
1822+ *
1823+ * This program is distributed in the hope that it will be useful,
1824+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1825+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1826+ * GNU General Public License for more details.
1827+ *
1828+ * You should have received a copy of the GNU General Public License
1829+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1830+ *
1831+ */
1832+
1833+package autopkgtest
1834+
1835+import (
1836+ "os"
1837+ "path/filepath"
1838+ "strconv"
1839+)
1840+
1841+var commonSSHOptions = []string{"---", "ssh"}
1842+
1843+func kvmSSHOptions(imagePath string) []string {
1844+ return append(
1845+ commonSSHOptions,
1846+ []string{
1847+ "-s", "/usr/share/autopkgtest/ssh-setup/snappy",
1848+ "--", "-i", imagePath}...)
1849+}
1850+
1851+func remoteTestbedSSHOptions(testbedIP string, testbedPort int) []string {
1852+ options := []string{
1853+ "-H", testbedIP,
1854+ "-p", strconv.Itoa(testbedPort),
1855+ "-l", "ubuntu",
1856+ "-i", filepath.Join(os.Getenv("HOME"), ".ssh", "id_rsa"),
1857+ "--reboot"}
1858+ return append(commonSSHOptions, options...)
1859+}
1860
1861=== added directory '_integration-tests/testutils/build'
1862=== added file '_integration-tests/testutils/build/build.go'
1863--- _integration-tests/testutils/build/build.go 1970-01-01 00:00:00 +0000
1864+++ _integration-tests/testutils/build/build.go 2015-08-02 12:28:52 +0000
1865@@ -0,0 +1,76 @@
1866+// -*- Mode: Go; indent-tabs-mode: t -*-
1867+
1868+/*
1869+ * Copyright (C) 2015 Canonical Ltd
1870+ *
1871+ * This program is free software: you can redistribute it and/or modify
1872+ * it under the terms of the GNU General Public License version 3 as
1873+ * published by the Free Software Foundation.
1874+ *
1875+ * This program is distributed in the hope that it will be useful,
1876+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1877+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1878+ * GNU General Public License for more details.
1879+ *
1880+ * You should have received a copy of the GNU General Public License
1881+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1882+ *
1883+ */
1884+
1885+package build
1886+
1887+import (
1888+ "fmt"
1889+ "os"
1890+
1891+ "launchpad.net/snappy/_integration-tests/testutils"
1892+)
1893+
1894+const (
1895+ // IntegrationTestName is the name of the test binary.
1896+ IntegrationTestName = "integration.test"
1897+ defaultGoArm = "7"
1898+ testsBinDir = "_integration-tests/bin/"
1899+)
1900+
1901+// Assets builds the snappy and integration tests binaries for the target
1902+// architecture.
1903+func Assets(useSnappyFromBranch bool, arch string) {
1904+ testutils.PrepareTargetDir(testsBinDir)
1905+
1906+ if useSnappyFromBranch {
1907+ // FIXME We need to build an image that has the snappy from the branch
1908+ // installed. --elopio - 2015-06-25.
1909+ buildSnappyCLI(arch)
1910+ }
1911+ buildTests(arch)
1912+}
1913+
1914+func buildSnappyCLI(arch string) {
1915+ fmt.Println("Building snappy CLI...")
1916+ // On the root of the project we have a directory called snappy, so we
1917+ // output the binary for the tests in the tests directory.
1918+ goCall(arch, "build", "-o", testsBinDir+"snappy", "./cmd/snappy")
1919+}
1920+
1921+func buildTests(arch string) {
1922+ fmt.Println("Building tests...")
1923+
1924+ goCall(arch, "test", "-c", "./_integration-tests/tests")
1925+ // XXX Go test 1.3 does not have the output flag, so we move the
1926+ // binaries after they are generated.
1927+ os.Rename("tests.test", testsBinDir+IntegrationTestName)
1928+}
1929+
1930+func goCall(arch string, cmds ...string) {
1931+ if arch != "" {
1932+ defer os.Setenv("GOARCH", os.Getenv("GOARCH"))
1933+ os.Setenv("GOARCH", arch)
1934+ if arch == "arm" {
1935+ defer os.Setenv("GOARM", os.Getenv("GOARM"))
1936+ os.Setenv("GOARM", defaultGoArm)
1937+ }
1938+ }
1939+ goCmd := append([]string{"go"}, cmds...)
1940+ testutils.ExecCommand(goCmd...)
1941+}
1942
1943=== added directory '_integration-tests/testutils/common'
1944=== added file '_integration-tests/testutils/common/common.go'
1945--- _integration-tests/testutils/common/common.go 1970-01-01 00:00:00 +0000
1946+++ _integration-tests/testutils/common/common.go 2015-08-02 12:28:52 +0000
1947@@ -0,0 +1,379 @@
1948+// -*- Mode: Go; indent-tabs-mode: t -*-
1949+
1950+/*
1951+ * Copyright (C) 2015 Canonical Ltd
1952+ *
1953+ * This program is free software: you can redistribute it and/or modify
1954+ * it under the terms of the GNU General Public License version 3 as
1955+ * published by the Free Software Foundation.
1956+ *
1957+ * This program is distributed in the hope that it will be useful,
1958+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1959+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1960+ * GNU General Public License for more details.
1961+ *
1962+ * You should have received a copy of the GNU General Public License
1963+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1964+ *
1965+ */
1966+
1967+package common
1968+
1969+import (
1970+ "fmt"
1971+ "io/ioutil"
1972+ "os"
1973+ "os/exec"
1974+ "path/filepath"
1975+ "regexp"
1976+ "strconv"
1977+ "strings"
1978+ "time"
1979+
1980+ check "gopkg.in/check.v1"
1981+
1982+ "launchpad.net/snappy/_integration-tests/testutils/config"
1983+)
1984+
1985+const (
1986+ // BaseAltPartitionPath is the path to the B system partition.
1987+ BaseAltPartitionPath = "/writable/cache/system"
1988+ needsRebootFile = "/tmp/needs-reboot"
1989+ channelCfgFile = "/etc/system-image/channel.ini"
1990+)
1991+
1992+// Cfg is a struct that contains the configuration values passed from the
1993+// host to the testbed.
1994+var Cfg *config.Config
1995+
1996+// SnappySuite is a structure used as a base test suite for all the snappy
1997+// integration tests.
1998+type SnappySuite struct {
1999+ cleanupHandlers []func()
2000+}
2001+
2002+// SetUpSuite disables the snappy autopilot. It will run before all the
2003+// integration suites.
2004+func (s *SnappySuite) SetUpSuite(c *check.C) {
2005+ ExecCommand(c, "sudo", "systemctl", "stop", "snappy-autopilot.timer")
2006+ ExecCommand(c, "sudo", "systemctl", "disable", "snappy-autopilot.timer")
2007+ if !isInRebootProcess() {
2008+ var err error
2009+ Cfg, err = config.ReadConfig(
2010+ "_integration-tests/data/output/testconfig.json")
2011+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2012+ if Cfg.Update || Cfg.Rollback {
2013+ switchSystemImageConf(c, Cfg.TargetRelease, Cfg.TargetChannel, "0")
2014+ // Always use the installed snappy because we are updating from an old
2015+ // image, so we should not use the snappy from the branch.
2016+ output := ExecCommand(c, "sudo", "/usr/bin/snappy", "update")
2017+ if output != "" {
2018+ RebootWithMark(c, "setupsuite-update")
2019+ }
2020+ }
2021+ } else if CheckRebootMark("setupsuite-update") {
2022+ RemoveRebootMark(c)
2023+ if Cfg.Rollback {
2024+ ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core")
2025+ RebootWithMark(c, "setupsuite-rollback")
2026+ }
2027+ } else if CheckRebootMark("setupsuite-rollback") {
2028+ RemoveRebootMark(c)
2029+ }
2030+}
2031+
2032+// SetUpTest handles reboots and stores version information. It will run before
2033+// all the integration tests. Before running a test, it will save the
2034+// ubuntu-core version. If a reboot was requested by a previous test, it
2035+// will skip all the following tests. If the suite is being called after the
2036+// test bed was rebooted, it will resume the test that requested the reboot.
2037+func (s *SnappySuite) SetUpTest(c *check.C) {
2038+ if NeedsReboot() {
2039+ contents, err := ioutil.ReadFile(needsRebootFile)
2040+ c.Assert(err, check.IsNil, check.Commentf("Error reading needs-reboot file %v", err))
2041+ c.Skip(fmt.Sprintf("****** Skipped %s during reboot caused by %s",
2042+ c.TestName(), contents))
2043+ } else {
2044+ if CheckRebootMark("") {
2045+ c.Logf("****** Running %s", c.TestName())
2046+ SetSavedVersion(c, GetCurrentUbuntuCoreVersion(c))
2047+ } else {
2048+ if AfterReboot(c) {
2049+ c.Logf("****** Resuming %s after reboot", c.TestName())
2050+ } else {
2051+ c.Skip(fmt.Sprintf("****** Skipped %s after reboot caused by %s",
2052+ c.TestName(), os.Getenv("ADT_REBOOT_MARK")))
2053+ }
2054+ }
2055+ }
2056+ // clear slice
2057+ s.cleanupHandlers = nil
2058+}
2059+
2060+// TearDownTest cleans up the channel.ini files in case they were changed by
2061+// the test.
2062+// It also runs the cleanup handlers
2063+func (s *SnappySuite) TearDownTest(c *check.C) {
2064+ if !NeedsReboot() && CheckRebootMark("") {
2065+ // Only restore the channel config files if the reboot has been handled.
2066+ m := make(map[string]string)
2067+ m[channelCfgBackupFile()] = "/"
2068+ m[channelCfgOtherBackupFile()] = BaseAltPartitionPath
2069+ for backup, target := range m {
2070+ if _, err := os.Stat(backup); err == nil {
2071+ MakeWritable(c, target)
2072+ defer MakeReadonly(c, target)
2073+ original := filepath.Join(target, channelCfgFile)
2074+ c.Logf("Restoring %s...", original)
2075+ ExecCommand(c, "sudo", "mv", backup, original)
2076+ }
2077+ }
2078+ }
2079+
2080+ // run cleanup handlers and clear the slice
2081+ for _, f := range s.cleanupHandlers {
2082+ f()
2083+ }
2084+ s.cleanupHandlers = nil
2085+}
2086+
2087+// AddCleanup adds a new cleanup function to the test
2088+func (s *SnappySuite) AddCleanup(f func()) {
2089+ s.cleanupHandlers = append(s.cleanupHandlers, f)
2090+}
2091+
2092+func switchSystemImageConf(c *check.C, release, channel, version string) {
2093+ targets := []string{"/", BaseAltPartitionPath}
2094+ for _, target := range targets {
2095+ file := filepath.Join(target, channelCfgFile)
2096+ if _, err := os.Stat(file); err == nil {
2097+ MakeWritable(c, target)
2098+ defer MakeReadonly(c, target)
2099+ replaceSystemImageValues(c, file, release, channel, version)
2100+ }
2101+ }
2102+}
2103+
2104+func replaceSystemImageValues(c *check.C, file, release, channel, version string) {
2105+ c.Log("Switching the system image conf...")
2106+ replaceRegex := map[string]string{
2107+ release: `s#channel: ubuntu-core/.*/\(.*\)#channel: ubuntu-core/%s/\1#`,
2108+ channel: `s#channel: ubuntu-core/\(.*\)/.*#channel: ubuntu-core/\1/%s#`,
2109+ version: `s/build_number: .*/build_number: %s/`,
2110+ }
2111+ for value, regex := range replaceRegex {
2112+ if value != "" {
2113+ ExecCommand(c,
2114+ "sudo", "sed", "-i", fmt.Sprintf(regex, value), file)
2115+ }
2116+ }
2117+ // Leave the new file in the test log.
2118+ ExecCommand(c, "cat", file)
2119+}
2120+
2121+func channelCfgBackupFile() string {
2122+ return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "channel.ini")
2123+}
2124+
2125+func channelCfgOtherBackupFile() string {
2126+ return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "channel.ini.other")
2127+}
2128+
2129+// ExecCommand executes a shell command and returns a string with the output
2130+// of the command. In case of error, it will fail the test.
2131+func ExecCommand(c *check.C, cmds ...string) string {
2132+ fmt.Println(strings.Join(cmds, " "))
2133+ cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
2134+ output, err := cmd.CombinedOutput()
2135+ stringOutput := string(output)
2136+ fmt.Print(stringOutput)
2137+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", stringOutput))
2138+ return stringOutput
2139+}
2140+
2141+// ExecCommandToFile executes a shell command and saves the output of the
2142+// command to a file. In case of error, it will fail the test.
2143+func ExecCommandToFile(c *check.C, filename string, cmds ...string) {
2144+ cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
2145+ outfile, err := os.Create(filename)
2146+ c.Assert(err, check.IsNil, check.Commentf("Error creating output file %s", filename))
2147+
2148+ defer outfile.Close()
2149+ cmd.Stdout = outfile
2150+
2151+ err = cmd.Run()
2152+ c.Assert(err, check.IsNil, check.Commentf("Error executing command '%v': %v", cmds, err))
2153+}
2154+
2155+// GetCurrentVersion returns the version of the installed and active package.
2156+func GetCurrentVersion(c *check.C, packageName string) string {
2157+ output := ExecCommand(c, "snappy", "list")
2158+ pattern := "(?mU)^" + packageName + " +(.*)$"
2159+ re := regexp.MustCompile(pattern)
2160+ match := re.FindStringSubmatch(string(output))
2161+ c.Assert(match, check.NotNil, check.Commentf("Version not found in %s", output))
2162+
2163+ // match is like "ubuntu-core 2015-06-18 93 ubuntu"
2164+ items := strings.Fields(match[0])
2165+ return items[2]
2166+}
2167+
2168+// GetCurrentUbuntuCoreVersion returns the version number of the installed and
2169+// active ubuntu-core.
2170+func GetCurrentUbuntuCoreVersion(c *check.C) int {
2171+ versionString := GetCurrentVersion(c, "ubuntu-core")
2172+ version, err := strconv.Atoi(versionString)
2173+ c.Assert(err, check.IsNil, check.Commentf("Error converting version to int %v", version))
2174+ return version
2175+}
2176+
2177+// CallFakeUpdate calls snappy update after faking the current version
2178+func CallFakeUpdate(c *check.C) string {
2179+ c.Log("Preparing fake and calling update.")
2180+ fakeAvailableUpdate(c)
2181+ return ExecCommand(c, "sudo", "snappy", "update")
2182+}
2183+
2184+func fakeAvailableUpdate(c *check.C) {
2185+ c.Log("Faking an available update...")
2186+ currentVersion := GetCurrentUbuntuCoreVersion(c)
2187+ switchChannelVersionWithBackup(c, currentVersion-1)
2188+ SetSavedVersion(c, currentVersion-1)
2189+}
2190+
2191+func switchChannelVersionWithBackup(c *check.C, newVersion int) {
2192+ m := make(map[string]string)
2193+ m["/"] = channelCfgBackupFile()
2194+ m[BaseAltPartitionPath] = channelCfgOtherBackupFile()
2195+ for target, backup := range m {
2196+ file := filepath.Join(target, channelCfgFile)
2197+ if _, err := os.Stat(file); err == nil {
2198+ MakeWritable(c, target)
2199+ defer MakeReadonly(c, target)
2200+ // Back up the file. It will be restored during the test tear down.
2201+ ExecCommand(c, "cp", file, backup)
2202+ replaceSystemImageValues(c, file, "", "", strconv.Itoa(newVersion))
2203+ }
2204+ }
2205+}
2206+
2207+// MakeWritable remounts a path with read and write permissions.
2208+func MakeWritable(c *check.C, path string) {
2209+ ExecCommand(c, "sudo", "mount", "-o", "remount,rw", path)
2210+}
2211+
2212+// MakeReadonly remounts a path with only read permissions.
2213+func MakeReadonly(c *check.C, path string) {
2214+ ExecCommand(c, "sudo", "mount", "-o", "remount,ro", path)
2215+}
2216+
2217+// Reboot requests a reboot using the test name as the mark.
2218+func Reboot(c *check.C) {
2219+ RebootWithMark(c, c.TestName())
2220+}
2221+
2222+// RebootWithMark requests a reboot using a specified mark.
2223+func RebootWithMark(c *check.C, mark string) {
2224+ c.Log("Preparing reboot with mark " + mark)
2225+ err := ioutil.WriteFile(needsRebootFile, []byte(mark), 0777)
2226+ c.Assert(err, check.IsNil, check.Commentf("Error writing needs-reboot file: %v", err))
2227+}
2228+
2229+// NeedsReboot returns True if a reboot has been requested by a test.
2230+func NeedsReboot() bool {
2231+ _, err := os.Stat(needsRebootFile)
2232+ return err == nil
2233+}
2234+
2235+// BeforeReboot returns True if the test is running before the test bed has
2236+// been rebooted, or after the test that requested the reboot handled it.
2237+func BeforeReboot() bool {
2238+ return CheckRebootMark("")
2239+}
2240+
2241+// AfterReboot returns True if the test is running after the test bed has been
2242+// rebooted.
2243+func AfterReboot(c *check.C) bool {
2244+ // $ADT_REBOOT_MARK contains the reboot mark, if we have rebooted it'll be the test name
2245+ return strings.HasPrefix(os.Getenv("ADT_REBOOT_MARK"), c.TestName())
2246+}
2247+
2248+// CheckRebootMark returns True if the reboot mark matches the string passed as
2249+// argument.
2250+func CheckRebootMark(mark string) bool {
2251+ return os.Getenv("ADT_REBOOT_MARK") == mark
2252+}
2253+
2254+func isInRebootProcess() bool {
2255+ return !CheckRebootMark("") || NeedsReboot()
2256+}
2257+
2258+// RemoveRebootMark removes the reboot mark to signal that the reboot has been
2259+// handled.
2260+func RemoveRebootMark(c *check.C) {
2261+ os.Setenv("ADT_REBOOT_MARK", "")
2262+}
2263+
2264+// SetSavedVersion saves a version number into a file so it can be used on
2265+// tests after reboots.
2266+func SetSavedVersion(c *check.C, version int) {
2267+ versionFile := getVersionFile()
2268+ err := ioutil.WriteFile(versionFile, []byte(strconv.Itoa(version)), 0777)
2269+ c.Assert(err, check.IsNil, check.Commentf("Error writing version file %s with %s", versionFile, version))
2270+}
2271+
2272+// GetSavedVersion returns the saved version number.
2273+func GetSavedVersion(c *check.C) int {
2274+ versionFile := getVersionFile()
2275+ contents, err := ioutil.ReadFile(versionFile)
2276+ c.Assert(err, check.IsNil, check.Commentf("Error reading version file %s", versionFile))
2277+
2278+ version, err := strconv.Atoi(string(contents))
2279+ c.Assert(err, check.IsNil, check.Commentf("Error converting version %v", contents))
2280+
2281+ return version
2282+}
2283+
2284+func getVersionFile() string {
2285+ return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "version")
2286+}
2287+
2288+// InstallSnap executes the required command to install the specified snap
2289+func InstallSnap(c *check.C, packageName string) string {
2290+ return ExecCommand(c, "sudo", "snappy", "install", packageName, "--allow-unauthenticated")
2291+}
2292+
2293+// RemoveSnap executes the required command to remove the specified snap
2294+func RemoveSnap(c *check.C, packageName string) string {
2295+ return ExecCommand(c, "sudo", "snappy", "remove", packageName)
2296+}
2297+
2298+// WaitForActiveService keeps asking for the active state of the given service until
2299+// it is active or the maximun waiting time expires, in which case an error is returned
2300+func WaitForActiveService(c *check.C, serviceName string) error {
2301+ maxWait := time.Second * 10
2302+ checkInterval := time.Millisecond * 500
2303+
2304+ timer := time.NewTimer(maxWait)
2305+ timeChan := timer.C
2306+
2307+ ticker := time.NewTicker(checkInterval)
2308+ tickChan := ticker.C
2309+
2310+ for {
2311+ select {
2312+ case <-timeChan:
2313+ ticker.Stop()
2314+ journalctlOutput := ExecCommand(c, "sudo", "journalctl", "-u", serviceName)
2315+ return fmt.Errorf("Service %s not active after %s, journalctl output: %s",
2316+ serviceName, maxWait, journalctlOutput)
2317+ case <-tickChan:
2318+ statusOutput := ExecCommand(
2319+ c, "systemctl", "show", "-p", "ActiveState", serviceName)
2320+ if statusOutput == "ActiveState=active\n" {
2321+ timer.Stop()
2322+ return nil
2323+ }
2324+ }
2325+ }
2326+}
2327
2328=== added file '_integration-tests/testutils/common/common_test.go'
2329--- _integration-tests/testutils/common/common_test.go 1970-01-01 00:00:00 +0000
2330+++ _integration-tests/testutils/common/common_test.go 2015-08-02 12:28:52 +0000
2331@@ -0,0 +1,68 @@
2332+// -*- Mode: Go; indent-tabs-mode: t -*-
2333+
2334+/*
2335+ * Copyright (C) 2014-2015 Canonical Ltd
2336+ *
2337+ * This program is free software: you can redistribute it and/or modify
2338+ * it under the terms of the GNU General Public License version 3 as
2339+ * published by the Free Software Foundation.
2340+ *
2341+ * This program is distributed in the hope that it will be useful,
2342+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2343+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2344+ * GNU General Public License for more details.
2345+ *
2346+ * You should have received a copy of the GNU General Public License
2347+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2348+ *
2349+ */
2350+
2351+package common
2352+
2353+import (
2354+ "testing"
2355+
2356+ . "gopkg.in/check.v1"
2357+)
2358+
2359+// Hook up check.v1 into the "go test" runner
2360+func Test(t *testing.T) { TestingT(t) }
2361+
2362+// testing a testsuite - thats so meta
2363+type MetaTestSuite struct {
2364+}
2365+
2366+var _ = Suite(&MetaTestSuite{})
2367+
2368+// test trivial cleanup
2369+func (m *MetaTestSuite) TestCleanupSimple(c *C) {
2370+ canary := "not-called"
2371+ s := SnappySuite{}
2372+
2373+ s.AddCleanup(func() {
2374+ canary = "was-called"
2375+ })
2376+ s.TearDownTest(c)
2377+
2378+ c.Assert(canary, Equals, "was-called")
2379+}
2380+
2381+// a mock method that takes a parameter
2382+func mockCleanupMethodWithParameters(s *string) {
2383+ *s = "was-called"
2384+}
2385+
2386+// test that whle AddCleanup() does not take any parameters itself,
2387+// functions that need parameters can be passed by creating an
2388+// anonymous function as a wrapper
2389+func (m *MetaTestSuite) TestCleanupWithParameters(c *C) {
2390+ canary := "not-called"
2391+ s := SnappySuite{}
2392+
2393+ s.AddCleanup(func() {
2394+ mockCleanupMethodWithParameters(&canary)
2395+ })
2396+ s.TearDownTest(c)
2397+
2398+ c.Assert(canary, Equals, "was-called")
2399+}
2400
2401=== added directory '_integration-tests/testutils/config'
2402=== added file '_integration-tests/testutils/config/config.go'
2403--- _integration-tests/testutils/config/config.go 1970-01-01 00:00:00 +0000
2404+++ _integration-tests/testutils/config/config.go 2015-08-02 12:28:52 +0000
2405@@ -0,0 +1,75 @@
2406+// -*- Mode: Go; indent-tabs-mode: t -*-
2407+
2408+/*
2409+ * Copyright (C) 2015 Canonical Ltd
2410+ *
2411+ * This program is free software: you can redistribute it and/or modify
2412+ * it under the terms of the GNU General Public License version 3 as
2413+ * published by the Free Software Foundation.
2414+ *
2415+ * This program is distributed in the hope that it will be useful,
2416+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2417+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2418+ * GNU General Public License for more details.
2419+ *
2420+ * You should have received a copy of the GNU General Public License
2421+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2422+ *
2423+ */
2424+
2425+package config
2426+
2427+import (
2428+ "encoding/json"
2429+ "fmt"
2430+ "io/ioutil"
2431+ "log"
2432+)
2433+
2434+// Config contains the values to pass to the test bed from the host.
2435+type Config struct {
2436+ FileName string
2437+ Release string
2438+ Channel string
2439+ TargetRelease string
2440+ TargetChannel string
2441+ RemoteTestbed bool
2442+ Update bool
2443+ Rollback bool
2444+}
2445+
2446+// NewConfig is the Config constructor
2447+func NewConfig(fileName, release, channel, targetRelease, targetChannel string, remoteTestbed, update, rollback bool) *Config {
2448+ return &Config{
2449+ FileName: fileName, Release: release, Channel: channel,
2450+ TargetRelease: targetRelease, TargetChannel: targetChannel,
2451+ RemoteTestbed: remoteTestbed, Update: update, Rollback: rollback,
2452+ }
2453+}
2454+
2455+// Write writes the config to a file that will be copied to the test bed.
2456+func (cfg Config) Write() {
2457+ fmt.Println("Writing test config...")
2458+ fmt.Println(cfg)
2459+ encoded, err := json.Marshal(cfg)
2460+ if err != nil {
2461+ log.Panicf("Error encoding the test config: %v", err)
2462+ }
2463+ err = ioutil.WriteFile(cfg.FileName, encoded, 0644)
2464+ if err != nil {
2465+ log.Panicf("Error writing the test config: %v", err)
2466+ }
2467+}
2468+
2469+// ReadConfig the config from a file
2470+func ReadConfig(fileName string) (*Config, error) {
2471+ b, err := ioutil.ReadFile(fileName)
2472+ if err != nil {
2473+ return nil, err
2474+ }
2475+ var decoded Config
2476+ if err = json.Unmarshal(b, &decoded); err != nil {
2477+ return nil, err
2478+ }
2479+ return &decoded, nil
2480+}
2481
2482=== added file '_integration-tests/testutils/config/config.test'
2483Binary files _integration-tests/testutils/config/config.test 1970-01-01 00:00:00 +0000 and _integration-tests/testutils/config/config.test 2015-08-02 12:28:52 +0000 differ
2484=== added file '_integration-tests/testutils/config/config_test.go'
2485--- _integration-tests/testutils/config/config_test.go 1970-01-01 00:00:00 +0000
2486+++ _integration-tests/testutils/config/config_test.go 2015-08-02 12:28:52 +0000
2487@@ -0,0 +1,121 @@
2488+// -*- Mode: Go; indent-tabs-mode: t -*-
2489+
2490+/*
2491+ * Copyright (C) 2014-2015 Canonical Ltd
2492+ *
2493+ * This program is free software: you can redistribute it and/or modify
2494+ * it under the terms of the GNU General Public License version 3 as
2495+ * published by the Free Software Foundation.
2496+ *
2497+ * This program is distributed in the hope that it will be useful,
2498+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2499+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2500+ * GNU General Public License for more details.
2501+ *
2502+ * You should have received a copy of the GNU General Public License
2503+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2504+ *
2505+ */
2506+
2507+package config
2508+
2509+import (
2510+ "fmt"
2511+ "io/ioutil"
2512+ "os"
2513+ "path/filepath"
2514+ "testing"
2515+
2516+ check "gopkg.in/check.v1"
2517+)
2518+
2519+// Hook up check.v1 into the "go test" runner
2520+func Test(t *testing.T) { check.TestingT(t) }
2521+
2522+type ConfigSuite struct{}
2523+
2524+var _ = check.Suite(&ConfigSuite{})
2525+
2526+func testConfigFileName(c *check.C) string {
2527+ tmpDir, err := ioutil.TempDir("", "")
2528+ c.Assert(err, check.IsNil, check.Commentf(
2529+ "Error creating a temporary directory: %v", err))
2530+ return filepath.Join(tmpDir, "test.config")
2531+}
2532+
2533+func testConfigStruct(fileName string) *Config {
2534+ return NewConfig(
2535+ fileName,
2536+ "testrelease", "testchannel", "testtargetrelease", "testtargetchannel",
2537+ true, true, true)
2538+}
2539+func testConfigContents(fileName string) string {
2540+ return `{` +
2541+ fmt.Sprintf(`"FileName":"%s",`, fileName) +
2542+ `"Release":"testrelease",` +
2543+ `"Channel":"testchannel",` +
2544+ `"TargetRelease":"testtargetrelease",` +
2545+ `"TargetChannel":"testtargetchannel",` +
2546+ `"RemoteTestbed":true,` +
2547+ `"Update":true,` +
2548+ `"Rollback":true` +
2549+ `}`
2550+}
2551+
2552+func (s *ConfigSuite) TestWriteConfig(c *check.C) {
2553+ // Do not print to stdout.
2554+ devnull, err := os.Open(os.DevNull)
2555+ c.Assert(err, check.IsNil)
2556+ oldStdout := os.Stdout
2557+ os.Stdout = devnull
2558+ defer func() {
2559+ os.Stdout = oldStdout
2560+ }()
2561+ configFileName := testConfigFileName(c)
2562+
2563+ cfg := testConfigStruct(configFileName)
2564+ cfg.Write()
2565+
2566+ writtenConfig, err := ioutil.ReadFile(configFileName)
2567+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2568+ c.Assert(string(writtenConfig), check.Equals, testConfigContents(configFileName))
2569+}
2570+
2571+func (s *ConfigSuite) TestReadConfig(c *check.C) {
2572+ configFileName := testConfigFileName(c)
2573+
2574+ configContents := testConfigContents(configFileName)
2575+ ioutil.WriteFile(configFileName, []byte(configContents), 0644)
2576+
2577+ cfg, err := ReadConfig(configFileName)
2578+
2579+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2580+ c.Assert(cfg, check.DeepEquals, testConfigStruct(configFileName))
2581+}
2582+
2583+func (s *ConfigSuite) TestReadConfigLocalTestBed(c *check.C) {
2584+ configFileName := testConfigFileName(c)
2585+
2586+ configContents := `{` +
2587+ fmt.Sprintf(`"FileName":"%s",`, configFileName) +
2588+ `"Release":"testrelease",` +
2589+ `"Channel":"testchannel",` +
2590+ `"TargetRelease":"testtargetrelease",` +
2591+ `"TargetChannel":"testtargetchannel",` +
2592+ `"RemoteTestbed":false,` +
2593+ `"Update":true,` +
2594+ `"Rollback":true` +
2595+ `}`
2596+
2597+ ioutil.WriteFile(configFileName, []byte(configContents), 0644)
2598+
2599+ cfg, err := ReadConfig(configFileName)
2600+
2601+ testConfigStruct := NewConfig(
2602+ configFileName,
2603+ "testrelease", "testchannel", "testtargetrelease", "testtargetchannel",
2604+ false, true, true)
2605+
2606+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2607+ c.Assert(cfg, check.DeepEquals, testConfigStruct)
2608+}
2609
2610=== added directory '_integration-tests/testutils/image'
2611=== added file '_integration-tests/testutils/image/image.go'
2612--- _integration-tests/testutils/image/image.go 1970-01-01 00:00:00 +0000
2613+++ _integration-tests/testutils/image/image.go 2015-08-02 12:28:52 +0000
2614@@ -0,0 +1,86 @@
2615+// -*- Mode: Go; indent-tabs-mode: t -*-
2616+
2617+/*
2618+ * Copyright (C) 2015 Canonical Ltd
2619+ *
2620+ * This program is free software: you can redistribute it and/or modify
2621+ * it under the terms of the GNU General Public License version 3 as
2622+ * published by the Free Software Foundation.
2623+ *
2624+ * This program is distributed in the hope that it will be useful,
2625+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2626+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2627+ * GNU General Public License for more details.
2628+ *
2629+ * You should have received a copy of the GNU General Public License
2630+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2631+ *
2632+ */
2633+
2634+package image
2635+
2636+import (
2637+ "fmt"
2638+ "path/filepath"
2639+ "strings"
2640+
2641+ "launchpad.net/snappy/_integration-tests/testutils"
2642+)
2643+
2644+// Image type encapsulates image actions
2645+type Image struct {
2646+ release string
2647+ channel string
2648+ revision string
2649+ baseDir string
2650+}
2651+
2652+// NewImage is the Image constructor
2653+func NewImage(release, channel, revision, baseDir string) *Image {
2654+ return &Image{release: release, channel: channel, revision: revision, baseDir: baseDir}
2655+}
2656+
2657+// UdfCreate forms and executes the UDF command for creating the image
2658+func (img Image) UdfCreate() (string, error) {
2659+ fmt.Println("Creating image...")
2660+
2661+ imageDir := filepath.Join(img.baseDir, "image")
2662+
2663+ testutils.PrepareTargetDir(imageDir)
2664+
2665+ udfCommand := []string{"sudo", "ubuntu-device-flash", "--verbose"}
2666+
2667+ if img.revision != "" {
2668+ udfCommand = append(udfCommand, "--revision", img.revision)
2669+ }
2670+
2671+ imagePath := img.imagePath(imageDir)
2672+
2673+ coreOptions := []string{
2674+ "core", img.release,
2675+ "--output", imagePath,
2676+ "--channel", img.channel,
2677+ "--developer-mode",
2678+ }
2679+
2680+ err := testutils.ExecCommand(append(udfCommand, coreOptions...)...)
2681+
2682+ return imagePath, err
2683+}
2684+
2685+func (img Image) imagePath(imageDir string) string {
2686+ revisionTag := img.revision
2687+ if revisionTag == "" {
2688+ revisionTag = "latest"
2689+ }
2690+
2691+ imageName := strings.Join(
2692+ []string{"snappy", img.release, img.channel, revisionTag}, "-") + ".img"
2693+
2694+ return filepath.Join(imageDir, imageName)
2695+}
2696+
2697+// SetRevision is the setter method for revision
2698+func (img Image) SetRevision(rev string) {
2699+ img.revision = rev
2700+}
2701
2702=== added file '_integration-tests/testutils/testutils.go'
2703--- _integration-tests/testutils/testutils.go 1970-01-01 00:00:00 +0000
2704+++ _integration-tests/testutils/testutils.go 2015-08-02 12:28:52 +0000
2705@@ -0,0 +1,61 @@
2706+// -*- Mode: Go; indent-tabs-mode: t -*-
2707+
2708+/*
2709+ * Copyright (C) 2015 Canonical Ltd
2710+ *
2711+ * This program is free software: you can redistribute it and/or modify
2712+ * it under the terms of the GNU General Public License version 3 as
2713+ * published by the Free Software Foundation.
2714+ *
2715+ * This program is distributed in the hope that it will be useful,
2716+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2717+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2718+ * GNU General Public License for more details.
2719+ *
2720+ * You should have received a copy of the GNU General Public License
2721+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2722+ *
2723+ */
2724+
2725+package testutils
2726+
2727+import (
2728+ "fmt"
2729+ "log"
2730+ "os"
2731+ "os/exec"
2732+ "strings"
2733+)
2734+
2735+// PrepareTargetDir creates the given target directory, removing it previously if it didn't exist
2736+func PrepareTargetDir(targetDir string) {
2737+ if _, err := os.Stat(targetDir); err == nil {
2738+ // dir exists, remove it
2739+ os.RemoveAll(targetDir)
2740+ }
2741+ os.MkdirAll(targetDir, 0777)
2742+}
2743+
2744+// RootPath return the test current working directory.
2745+func RootPath() string {
2746+ dir, err := os.Getwd()
2747+ if err != nil {
2748+ log.Panic(err)
2749+ }
2750+ return dir
2751+}
2752+
2753+// ExecCommand executes the given command and pipes the results to os.Stdout and os.Stderr, returning the resulting error
2754+func ExecCommand(cmds ...string) error {
2755+ fmt.Println(strings.Join(cmds, " "))
2756+
2757+ cmd := exec.Command(cmds[0], cmds[1:]...)
2758+ cmd.Stdout = os.Stdout
2759+ cmd.Stderr = os.Stderr
2760+
2761+ err := cmd.Run()
2762+ if err != nil {
2763+ log.Panicf("Error while running %s: %s\n", cmd.Args, err)
2764+ }
2765+ return err
2766+}
2767
2768=== modified file 'run-checks'
2769--- run-checks 2015-03-27 18:35:24 +0000
2770+++ run-checks 2015-08-02 12:28:52 +0000
2771@@ -13,7 +13,7 @@
2772
2773 if [ -n "$fmt" ]; then
2774 echo "Formatting wrong in following files"
2775- echo $fmt
2776+ echo "$fmt"
2777 exit 1
2778 fi
2779
2780@@ -42,14 +42,30 @@
2781 # go vet
2782 echo Running vet
2783 go vet ./...
2784+go vet ./_integration-tests/tests/...
2785+go vet ./_integration-tests/testutils/...
2786
2787 # golint
2788 echo Running lint
2789-lint=$(golint ./...)
2790+lint=$(golint ./... && golint ./_integration-tests/testutils/... && golint ./_integration-tests/tests/...)
2791 if [ -n "$lint" ]; then
2792 echo "Lint complains:"
2793- echo $lint
2794+ echo "$lint"
2795 exit 1
2796 fi
2797
2798+# integration tests
2799+echo Building the integration tests
2800+go build _integration-tests/main.go
2801+
2802+# the rabbit hole
2803+echo Running the tests for the integration testutils
2804+$goctest -v -cover ./_integration-tests/testutils/...
2805+
2806+# integration suite in kvm
2807+if which adt-run >/dev/null 2>&1; then
2808+ echo "Running integration tests on 15.04 edge"
2809+ go run _integration-tests/main.go --snappy-from-branch --release=15.04
2810+fi
2811+
2812 echo "All good, what could possibly go wrong"

Subscribers

People subscribed via source and target branches

to all changes: