]> xenbits.xensource.com Git - people/gdunlap/xsatool.git/commitdiff
Change to in-binary test
authorGeorge Dunlap <george.dunlap@citrix.com>
Fri, 19 May 2017 16:05:44 +0000 (17:05 +0100)
committerGeorge Dunlap <george.dunlap@citrix.com>
Fri, 19 May 2017 16:21:09 +0000 (17:21 +0100)
`go test` apparently has a default 10-minute timeout.  This isn't
enough for all the tests in the "system test", and there's no way to
change the default except to manually add a --timeout parameter to "go
test".

Instead pull the test out into a special xsatool command.

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
main.go
system_test.go [deleted file]
systemtest.go [new file with mode: 0644]

diff --git a/main.go b/main.go
index d8d87c998fd21f9a8793809d6c0978cbeaf4c1cd..3bc516776eac04fb3d27e2f2665a979aa3ffb60f 100644 (file)
--- a/main.go
+++ b/main.go
@@ -66,7 +66,7 @@ func globalReset() {
 }
 
 func XsaMain(args []string) int {
-       if len(args) < 3 {
+       if len(args) < 3 && args[1] != "systemtest" {
                fmt.Printf("Not enough arguments\n")
                return 1
        }
@@ -84,8 +84,11 @@ func XsaMain(args []string) int {
 
        tgt := args[1]
        args = args[2:]
-       cmd := args[0]
-       args = args[1:]
+       var cmd string
+       if len(args) > 0 {
+               cmd = args[0]
+               args = args[1:]
+       }
 
        if xsanum, err = strconv.Atoi(tgt); err == nil {
                switch cmd {
@@ -145,6 +148,8 @@ func XsaMain(args []string) int {
                        fmt.Printf("Unknown command: %s\n", cmd)
                        return 1
                }
+       } else if tgt == "systemtest" {
+               return MainSystemTest()
        }
 
        if loadConfig {
@@ -215,6 +220,7 @@ func main() {
 }
 
 func MainHarness(args ...string) int {
+       Q.real = false
        x := append([]string{"xsatool"}, args...)
        return XsaMain(x)
 }
diff --git a/system_test.go b/system_test.go
deleted file mode 100644 (file)
index 9d1b846..0000000
+++ /dev/null
@@ -1,430 +0,0 @@
-package main
-
-import (
-       "fmt"
-       "os"
-       "path/filepath"
-       "testing"
-)
-
-// Should we always do a new full clone (slow) or use a previous clone
-// if it exists (and leave clones for future callers)?
-var FullClone = false
-
-// Set up the repo in a known state.
-func InitRepo(t *testing.T) bool {
-       callRepoInit := true
-
-       if FullClone {
-               // Make temporary directory and cd to it
-               // rm -rf tmp
-               if err := os.RemoveAll("tmp"); err != nil {
-                       t.Errorf("Removing temporary path: %v", err)
-                       return false
-               }
-       } else {
-               // Check to see if the directory exists
-               _, err := os.Stat("tmp")
-               if err == nil {
-                       callRepoInit = false
-               } else {
-                       if !os.IsNotExist(err) {
-                               t.Errorf("Stat'ing temporary directory: %v\n", err)
-                               return false
-                       }
-               }
-       }
-
-       if callRepoInit {
-               // mkdir tmp
-               if err := os.Mkdir("tmp", 0777); err != nil {
-                       t.Errorf("Making temporary path: %v\n", err)
-                       return false
-               }
-       }
-
-       if err := os.Chdir("tmp"); err != nil {
-               t.Errorf("cd tmp: %v\n", err)
-               return false
-       }
-
-       if callRepoInit {
-               // Initialize a current version of the repo
-               if ret := MainHarness("repo", "init"); ret != 0 {
-                       t.Errorf("repo init failed: %d\n", ret)
-                       return false
-               }
-       } else {
-               // Read existing version of repos
-               if ret := MainHarness("repo", "info"); ret != 0 {
-                       t.Errorf("repo info failed: %d\n", ret)
-                       return false
-               }
-       }
-
-       // Reset to a known state:
-       // (Want to be able to apply XSA-206 (backports) , XSA-211 (qemu), XSA-212-15 (xen)
-       // ~ March 27th
-       // Xen master    : ac9ff74
-       // qemu-upstream : acde9f3
-       // qemu-trad     : 8b4834e
-       // xsa           : bff3883
-       master := map[Tree]string{
-               "xen":   "ac9ff74",
-               "qemuu": "acde9f3",
-               "qemut": "8b4834e",
-               "xsa":   "bff3883",
-       }
-
-       updateMaster := func(t Tree) (err error) {
-               var r *Repo
-               if t == TreeXSA {
-                       r = &G.repos.xsa
-               } else {
-                       xr, prs := G.repos.XenRepos[t]
-                       if !prs {
-                               err = fmt.Errorf("Not present: %s\n", t)
-                               return
-                       }
-                       r = &xr.Repo
-               }
-
-               _, err = r.ResetBranch("master", master[t])
-               return
-       }
-
-       if err := ForEachTree(updateMaster); err != nil {
-               t.Errorf("Tree update failed: %v\n", err)
-               return false
-       }
-
-       // Delete:
-       // All xenversions > 4.8
-       //  4.8.0, 4.7.2
-       // (4.6 and previous will have no new versions)
-       vlimit := map[XenVersion]XenVersionFull{
-               "4.8": "4.8.0",
-               "4.7": "4.7.2",
-       }
-       r := G.repos.XenRepos[TreeXen]
-       versions, err := r.GetVersions()
-       if err != nil {
-               t.Errorf("Error getting Xen releases: %v\n", err)
-               return false
-       }
-
-       // FIXME: Grab 4.8.1 ref so we can re-tag it and test repo update
-       for _, fv := range versions {
-               l, prs := vlimit[fv.XenVersion()]
-               if fv.XenVersion().IsGreaterEqualThan(XenVersion("4.9")) ||
-                       (prs && fv.Point() > l.Point()) {
-                       tag := "RELEASE-" + string(fv)
-                       t.Logf("Deleting tag %s\n", tag)
-                       if err := r.DeleteTag(tag); err != nil {
-                               t.Errorf("Deleting tag %s: %v\n", tag, err)
-                               return false
-                       }
-               }
-       }
-
-       // Delete files potentially left over from previous runs
-       if !FullClone {
-               // rm xsa.git/*.meta
-               files, err := filepath.Glob("xsa.git/*.meta")
-               if err != nil {
-                       t.Errorf("Finding meta files to remove: %v", err)
-                       return false
-               }
-
-               for _, file := range files {
-                       t.Logf("Removing meta file %s\n", file)
-                       err = os.Remove(file)
-                       if err != nil {
-                               t.Errorf("Removing meta file %s: %v", file, err)
-                               return false
-                       }
-               }
-       }
-
-       // Test to make sure things are in the expected state
-
-       return true
-}
-
-func GlobalInit(t *testing.T) (pass bool) {
-       //args := []string{"xsatool", "global", "update"}
-       pass = true
-
-       QuerySetResponses([]string{"4.4"})
-       if MainHarness("global", "update") != 0 {
-               return false
-       }
-
-       // Check to see that we have the expected
-       versions := map[XenVersion]*struct {
-               fv   XenVersionFull
-               seen bool
-       }{
-               "master": {fv: "master"},
-               "4.8":    {fv: "4.8.0"},
-               "4.7":    {fv: "4.7.2"},
-               "4.6":    {fv: "4.6.5"},
-               "4.5":    {fv: "4.5.5"},
-               "4.4":    {fv: "4.4.4"},
-       }
-
-       for v := range G.config.Security.Versions {
-               _, prs := versions[v]
-               if !prs {
-                       t.Errorf("Unexpected version: %v\n", v)
-                       pass = false
-               } else {
-                       versions[v].seen = true
-                       if versions[v].fv != G.config.Security.Versions[v].Latest {
-                               t.Errorf("Expected fullversion %v, got %v\n",
-                                       versions[v].fv,
-                                       G.config.Security.Versions[v].Latest)
-                               pass = false
-                       }
-               }
-       }
-
-       for v := range versions {
-               if !versions[v].seen {
-                       t.Errorf("Didn't find expected xenversion %v\n", v)
-                       pass = false
-               }
-       }
-       return pass
-}
-
-func Story206Init(t *testing.T) (pass bool) {
-       t.Logf(" xsatool 206 init xen")
-       if MainHarness("206", "init", "xen") != 0 {
-               t.Errorf("xsatool 206 init failed\n")
-               return false
-       }
-
-       // Things to check
-       // - Recipes for appropriate
-       var xsa XSAMeta
-       if err := xsa.Load(206); err != nil {
-               t.Errorf("Re-loading xsa: %v\n", err)
-               return false
-       }
-
-       pass = true
-
-       if xsa.XSA != 206 {
-               t.Errorf("Unexpected XSA number: %d\n", xsa.XSA)
-               pass = false
-       }
-
-       if len(xsa.Trees) != 1 || xsa.Trees[0] != TreeXen {
-               t.Errorf("Unexpected xsa.Trees state: %v\n", xsa.Trees)
-               pass = false
-       }
-
-       versions := map[XenVersion]*struct {
-               fv   XenVersionFull
-               seen bool
-       }{
-               "master": {fv: "master"},
-               "4.8":    {fv: "4.8.0"},
-               "4.7":    {fv: "4.7.2"},
-               "4.6":    {fv: "4.6.5"},
-               "4.5":    {fv: "4.5.5"},
-               "4.4":    {fv: "4.4.4"},
-       }
-
-       for v := range xsa.Recipes {
-               r := xsa.Recipes[v]
-               _, prs := versions[v]
-               if !prs {
-                       t.Errorf("Unexpected version: %v\n", v)
-                       pass = false
-               } else {
-                       versions[v].seen = true
-                       if versions[v].fv != r.XenVersionFull {
-                               t.Errorf("Expected fullversion %v, got %v\n",
-                                       versions[v].fv,
-                                       r.XenVersionFull)
-                               pass = false
-                       }
-                       ForEachCodeTree(func(tree Tree) error {
-                               tr, prs := r.Recipes[tree]
-                               if tree == TreeXen {
-                                       if !prs {
-                                               t.Errorf("Missing expected recipe for Xen\n")
-                                               pass = false
-                                       } else if len(tr.Prereqs) != 0 || len(tr.Patches) != 0 {
-                                               t.Errorf("Unexpected recipe for tree Xen\n")
-                                               pass = false
-                                       }
-                               } else {
-                                       if prs {
-                                               t.Errorf("Unexpected tree: %v\n", tree)
-                                               pass = false
-                                       }
-                               }
-                               return nil
-                       })
-               }
-       }
-
-       for v := range versions {
-               if !versions[v].seen {
-                       t.Errorf("Didn't find expected xenversion %v\n", v)
-                       pass = false
-               }
-       }
-
-       sm := &G.config.Security
-       for _, vm := range sm.Versions {
-               ForEachCodeTree(func(tree Tree) error {
-                       xsas, prs := vm.XSAs[tree]
-                       if !prs {
-                               t.Errorf("No XSA list for tree %v\n", tree)
-                               pass = false
-                       } else {
-                               expected := 0
-                               if tree == TreeXen {
-                                       expected = 1
-                               }
-                               if len(xsas) != expected {
-                                       t.Errorf("Expected %d xsas, got %d!\n", expected, len(xsas))
-                                       pass = false
-                               }
-                       }
-                       return nil
-               })
-       }
-
-       // - Branches exist
-
-       return
-}
-
-func Story206Implement(t *testing.T) (pass bool) {
-       // [should already be on correct branch]
-
-       // git am $PATH/xsa206-master/*.patch
-
-       out, err := G.repos.XenRepos[TreeXen].Am("../testdata/xsa206-unstable/*.patch")
-       if err != nil {
-               t.Errorf("Error importing example patches: %v %s\n", err, string(out))
-               return
-       }
-
-       return true
-}
-
-func Story206SyncOne(t *testing.T) (pass bool) {
-       t.Logf(" xsatool 206 sync-patches")
-       if MainHarness("206", "sync-patches") != 0 {
-               t.Errorf("xsatool 206 sync-patches failed\n")
-               return false
-       }
-
-       var xsa XSAMeta
-
-       err := xsa.Load(206)
-       if err != nil {
-               t.Errorf("Error loading updated recipe: %v\n", err)
-               return false
-       }
-
-       pass = true
-
-       for v := range xsa.Recipes {
-               r := xsa.GetRecipe(v)
-               for tree := range r.Recipes {
-                       tr := r.Recipes[tree]
-                       expected := 0
-                       if v == XenVersion("master") && tree == TreeXen {
-                               expected = 1
-                       }
-                       if len(tr.Patches) != expected {
-                               t.Errorf("Version %v tree %v: Wanted %d, got %d\n",
-                                       v, tree, expected, len(tr.Patches))
-                               pass = false
-                       }
-               }
-       }
-
-       return pass
-}
-
-func Story206(t *testing.T) bool {
-       t.Logf("Starting XSA-206 'story'")
-       // xsatool 206 init xen
-       if !Story206Init(t) {
-               return false
-       }
-
-       // Fake-up xsa/206/master
-       if !Story206Implement(t) {
-               return false
-       }
-
-       // 206 sync-patches
-       if !Story206SyncOne(t) {
-               return false
-       }
-
-       // 206 Backport (should fail at 4.8. 4.7, and 4.6; NOT 4.5)
-
-       // 206 sync-patches
-
-       // 206 test
-
-       return true
-}
-
-func TestSystem(t *testing.T) {
-       if testing.Short() {
-               t.Skipf("Not running full system tests")
-       }
-
-       // Store the toplevel directory
-       topdir, err := os.Getwd()
-       if err != nil {
-               t.Errorf("Getwd failed?\n")
-               return
-       }
-
-       // Init and/or cd into the testing directory
-       if !InitRepo(t) {
-               t.Errorf("Could not initialize repo")
-               goto out
-       }
-
-       // Initialize global state
-       // xsatool global update
-       if !GlobalInit(t) {
-               goto out
-       }
-
-       //// xsa 206 "story"
-       if !Story206(t) {
-               goto out
-       }
-
-       //// xsa 211 "story"
-
-       //// Drop 4.4 support, update to 4.8.1
-
-       //// xsa 212-215 "story"
-
-out:
-       os.Chdir(topdir)
-
-       if FullClone {
-               // rm -rf tmp
-               if err := os.RemoveAll("tmp"); err != nil {
-                       t.Errorf("Removing temporary path: %v", err)
-                       return
-               }
-       }
-
-}
diff --git a/systemtest.go b/systemtest.go
new file mode 100644 (file)
index 0000000..8d1b287
--- /dev/null
@@ -0,0 +1,448 @@
+package main
+
+import (
+       "fmt"
+       "os"
+       "path/filepath"
+)
+
+type SystemTest struct {
+       failed bool
+}
+
+func (st *SystemTest) Errorf(s string, args ...interface{}) {
+       st.failed = true
+       fmt.Printf(s, args...)
+}
+
+// Should we always do a new full clone (slow) or use a previous clone
+// if it exists (and leave clones for future callers)?
+var FullClone = false
+
+// Set up the repo in a known state.
+func InitRepo(st *SystemTest) bool {
+       callRepoInit := true
+
+       if FullClone {
+               // Make temporary directory and cd to it
+               // rm -rf tmp
+               if err := os.RemoveAll("tmp"); err != nil {
+                       st.Errorf("Removing temporary path: %v", err)
+                       return false
+               }
+       } else {
+               // Check to see if the directory exists
+               _, err := os.Stat("tmp")
+               if err == nil {
+                       callRepoInit = false
+               } else {
+                       if !os.IsNotExist(err) {
+                               st.Errorf("Stat'ing temporary directory: %v\n", err)
+                               return false
+                       }
+               }
+       }
+
+       if callRepoInit {
+               // mkdir tmp
+               if err := os.Mkdir("tmp", 0777); err != nil {
+                       st.Errorf("Making temporary path: %v\n", err)
+                       return false
+               }
+       }
+
+       if err := os.Chdir("tmp"); err != nil {
+               st.Errorf("cd tmp: %v\n", err)
+               return false
+       }
+
+       if callRepoInit {
+               // Initialize a current version of the repo
+               if ret := MainHarness("repo", "init"); ret != 0 {
+                       st.Errorf("repo init failed: %d\n", ret)
+                       return false
+               }
+       } else {
+               // Read existing version of repos
+               if ret := MainHarness("repo", "info"); ret != 0 {
+                       st.Errorf("repo info failed: %d\n", ret)
+                       return false
+               }
+       }
+
+       // Reset to a known state:
+       // (Want to be able to apply XSA-206 (backports) , XSA-211 (qemu), XSA-212-15 (xen)
+       // ~ March 27th
+       // Xen master    : ac9ff74
+       // qemu-upstream : acde9f3
+       // qemu-trad     : 8b4834e
+       // xsa           : bff3883
+       master := map[Tree]string{
+               "xen":   "ac9ff74",
+               "qemuu": "acde9f3",
+               "qemut": "8b4834e",
+               "xsa":   "bff3883",
+       }
+
+       updateMaster := func(t Tree) (err error) {
+               var r *Repo
+               if t == TreeXSA {
+                       r = &G.repos.xsa
+               } else {
+                       xr, prs := G.repos.XenRepos[t]
+                       if !prs {
+                               err = fmt.Errorf("Not present: %s\n", t)
+                               return
+                       }
+                       r = &xr.Repo
+               }
+
+               _, err = r.ResetBranch("master", master[t])
+               return
+       }
+
+       if err := ForEachTree(updateMaster); err != nil {
+               st.Errorf("Tree update failed: %v\n", err)
+               return false
+       }
+
+       // Delete:
+       // All xenversions > 4.8
+       //  4.8.0, 4.7.2
+       // (4.6 and previous will have no new versions)
+       vlimit := map[XenVersion]XenVersionFull{
+               "4.8": "4.8.0",
+               "4.7": "4.7.2",
+       }
+       r := G.repos.XenRepos[TreeXen]
+       versions, err := r.GetVersions()
+       if err != nil {
+               st.Errorf("Error getting Xen releases: %v\n", err)
+               return false
+       }
+
+       // FIXME: Grab 4.8.1 ref so we can re-tag it and test repo update
+       for _, fv := range versions {
+               l, prs := vlimit[fv.XenVersion()]
+               if fv.XenVersion().IsGreaterEqualThan(XenVersion("4.9")) ||
+                       (prs && fv.Point() > l.Point()) {
+                       tag := "RELEASE-" + string(fv)
+                       fmt.Printf("Deleting tag %s\n", tag)
+                       if err := r.DeleteTag(tag); err != nil {
+                               st.Errorf("Deleting tag %s: %v\n", tag, err)
+                               return false
+                       }
+               }
+       }
+
+       // Delete files potentially left over from previous runs
+       if !FullClone {
+               // rm xsa.git/*.meta
+               files, err := filepath.Glob("xsa.git/*.meta")
+               if err != nil {
+                       st.Errorf("Finding meta files to remove: %v", err)
+                       return false
+               }
+
+               for _, file := range files {
+                       fmt.Printf("Removing meta file %s\n", file)
+                       err = os.Remove(file)
+                       if err != nil {
+                               st.Errorf("Removing meta file %s: %v", file, err)
+                               return false
+                       }
+               }
+       }
+
+       // Test to make sure things are in the expected state
+
+       return true
+}
+
+func GlobalInit(st *SystemTest) (pass bool) {
+       //args := []string{"xsatool", "global", "update"}
+       pass = true
+
+       QuerySetResponses([]string{"4.4"})
+       if MainHarness("global", "update") != 0 {
+               return false
+       }
+
+       // Check to see that we have the expected
+       versions := map[XenVersion]*struct {
+               fv   XenVersionFull
+               seen bool
+       }{
+               "master": {fv: "master"},
+               "4.8":    {fv: "4.8.0"},
+               "4.7":    {fv: "4.7.2"},
+               "4.6":    {fv: "4.6.5"},
+               "4.5":    {fv: "4.5.5"},
+               "4.4":    {fv: "4.4.4"},
+       }
+
+       for v := range G.config.Security.Versions {
+               _, prs := versions[v]
+               if !prs {
+                       st.Errorf("Unexpected version: %v\n", v)
+                       pass = false
+               } else {
+                       versions[v].seen = true
+                       if versions[v].fv != G.config.Security.Versions[v].Latest {
+                               st.Errorf("Expected fullversion %v, got %v\n",
+                                       versions[v].fv,
+                                       G.config.Security.Versions[v].Latest)
+                               pass = false
+                       }
+               }
+       }
+
+       for v := range versions {
+               if !versions[v].seen {
+                       st.Errorf("Didn't find expected xenversion %v\n", v)
+                       pass = false
+               }
+       }
+       return pass
+}
+
+func Story206Init(st *SystemTest) (pass bool) {
+       fmt.Printf(" xsatool 206 init xen")
+       if MainHarness("206", "init", "xen") != 0 {
+               st.Errorf("xsatool 206 init failed\n")
+               return false
+       }
+
+       // Things to check
+       // - Recipes for appropriate
+       var xsa XSAMeta
+       if err := xsa.Load(206); err != nil {
+               st.Errorf("Re-loading xsa: %v\n", err)
+               return false
+       }
+
+       pass = true
+
+       if xsa.XSA != 206 {
+               st.Errorf("Unexpected XSA number: %d\n", xsa.XSA)
+               pass = false
+       }
+
+       if len(xsa.Trees) != 1 || xsa.Trees[0] != TreeXen {
+               st.Errorf("Unexpected xsa.Trees state: %v\n", xsa.Trees)
+               pass = false
+       }
+
+       versions := map[XenVersion]*struct {
+               fv   XenVersionFull
+               seen bool
+       }{
+               "master": {fv: "master"},
+               "4.8":    {fv: "4.8.0"},
+               "4.7":    {fv: "4.7.2"},
+               "4.6":    {fv: "4.6.5"},
+               "4.5":    {fv: "4.5.5"},
+               "4.4":    {fv: "4.4.4"},
+       }
+
+       for v := range xsa.Recipes {
+               r := xsa.Recipes[v]
+               _, prs := versions[v]
+               if !prs {
+                       st.Errorf("Unexpected version: %v\n", v)
+                       pass = false
+               } else {
+                       versions[v].seen = true
+                       if versions[v].fv != r.XenVersionFull {
+                               st.Errorf("Expected fullversion %v, got %v\n",
+                                       versions[v].fv,
+                                       r.XenVersionFull)
+                               pass = false
+                       }
+                       ForEachCodeTree(func(tree Tree) error {
+                               tr, prs := r.Recipes[tree]
+                               if tree == TreeXen {
+                                       if !prs {
+                                               st.Errorf("Missing expected recipe for Xen\n")
+                                               pass = false
+                                       } else if len(tr.Prereqs) != 0 || len(tr.Patches) != 0 {
+                                               st.Errorf("Unexpected recipe for tree Xen\n")
+                                               pass = false
+                                       }
+                               } else {
+                                       if prs {
+                                               st.Errorf("Unexpected tree: %v\n", tree)
+                                               pass = false
+                                       }
+                               }
+                               return nil
+                       })
+               }
+       }
+
+       for v := range versions {
+               if !versions[v].seen {
+                       st.Errorf("Didn't find expected xenversion %v\n", v)
+                       pass = false
+               }
+       }
+
+       sm := &G.config.Security
+       for _, vm := range sm.Versions {
+               ForEachCodeTree(func(tree Tree) error {
+                       xsas, prs := vm.XSAs[tree]
+                       if !prs {
+                               st.Errorf("No XSA list for tree %v\n", tree)
+                               pass = false
+                       } else {
+                               expected := 0
+                               if tree == TreeXen {
+                                       expected = 1
+                               }
+                               if len(xsas) != expected {
+                                       st.Errorf("Expected %d xsas, got %d!\n", expected, len(xsas))
+                                       pass = false
+                               }
+                       }
+                       return nil
+               })
+       }
+
+       // - Branches exist
+
+       return
+}
+
+func Story206Implement(st *SystemTest) (pass bool) {
+       // [should already be on correct branch]
+
+       // git am $PATH/xsa206-master/*.patch
+
+       out, err := G.repos.XenRepos[TreeXen].Am("../testdata/xsa206-unstable/*.patch")
+       if err != nil {
+               st.Errorf("Error importing example patches: %v %s\n", err, string(out))
+               return
+       }
+
+       return true
+}
+
+func Story206SyncOne(st *SystemTest) (pass bool) {
+       fmt.Printf(" xsatool 206 sync-patches")
+       if MainHarness("206", "sync-patches") != 0 {
+               st.Errorf("xsatool 206 sync-patches failed\n")
+               return false
+       }
+
+       var xsa XSAMeta
+
+       err := xsa.Load(206)
+       if err != nil {
+               st.Errorf("Error loading updated recipe: %v\n", err)
+               return false
+       }
+
+       pass = true
+
+       for v := range xsa.Recipes {
+               r := xsa.GetRecipe(v)
+               for tree := range r.Recipes {
+                       tr := r.Recipes[tree]
+                       expected := 0
+                       if v == XenVersion("master") && tree == TreeXen {
+                               expected = 1
+                       }
+                       if len(tr.Patches) != expected {
+                               st.Errorf("Version %v tree %v: Wanted %d, got %d\n",
+                                       v, tree, expected, len(tr.Patches))
+                               pass = false
+                       }
+               }
+       }
+
+       return pass
+}
+
+func Story206(st *SystemTest) bool {
+       fmt.Printf("Starting XSA-206 'story'")
+       // xsatool 206 init xen
+       if !Story206Init(st) {
+               return false
+       }
+
+       // Fake-up xsa/206/master
+       if !Story206Implement(st) {
+               return false
+       }
+
+       // 206 sync-patches
+       if !Story206SyncOne(st) {
+               return false
+       }
+
+       // 206 Backport (should fail at 4.8. 4.7, and 4.6; NOT 4.5)
+
+       // 206 sync-patches
+
+       // 206 test
+
+       return true
+}
+
+func MainSystemTest() (ret int) {
+       var st SystemTest
+
+       // Fail unless we make it through the gauntlet
+       ret = 1
+       
+       // Store the toplevel directory
+       topdir, err := os.Getwd()
+       if err != nil {
+               st.Errorf("Getwd failed?\n")
+               return
+       }
+
+       // Init and/or cd into the testing directory
+       if !InitRepo(&st) {
+               st.Errorf("Could not initialize repo")
+               goto out
+       }
+
+       // Initialize global state
+       // xsatool global update
+       if !GlobalInit(&st) {
+               goto out
+       }
+
+       //// xsa 206 "story"
+       if !Story206(&st) {
+               goto out
+       }
+
+       //// xsa 211 "story"
+
+       //// Drop 4.4 support, update to 4.8.1
+
+       //// xsa 212-215 "story"
+
+       ret = 0
+out:
+       os.Chdir(topdir)
+
+       if FullClone {
+               // rm -rf tmp
+               if err := os.RemoveAll("tmp"); err != nil {
+                       st.Errorf("Removing temporary path: %v", err)
+                       ret = 1
+                       return
+               }
+       }
+
+       if st.failed {
+               fmt.Print("System test: FAILED\n")
+       } else {
+               fmt.Print("System test: PASSED\n")
+       }
+       
+       return
+}