summaryrefslogtreecommitdiff
path: root/yarns
diff options
context:
space:
mode:
authorLars Wirzenius <lars.wirzenius@codethink.co.uk>2013-07-29 14:15:11 +0000
committerLars Wirzenius <lars.wirzenius@codethink.co.uk>2013-07-30 17:10:08 +0000
commitfac47acd5ab8e3410929e8c36ceaa4ecf070de10 (patch)
treea880bdeeffc2d62804b97e85551132319258dae9 /yarns
parentb36072a5af5947e5dbb73395d821f1a57e0ef773 (diff)
downloadmorph-fac47acd5ab8e3410929e8c36ceaa4ecf070de10.tar.gz
Add scenario (yarn) tests for most of branching and merging
These scenarios test the basics of most of the subcommands the branch and merge plugin provides. They don't purport to be complete, but give some indication that things work, and form a basis upon which further things can be built. Yarn also isn't included in a Baserock release yet, so we need to keep the cmdtests until Baserock 10 has been released. The existing cmdtest tests are not modified by this: they are left intact, until they can analysed in detail for things to be added to the scenarios. After that, the cmdtest tests will start to go away. Merging is not covered by these tests: it is not clear how merge should work, and the current code is known to do the wrong thing in many cases. Scenarios for merge will be added later. Building is also not covered. Testing builds well needs some additional, careful thinking, and that isn't ready for this patch series. It will be added later.
Diffstat (limited to 'yarns')
-rw-r--r--yarns/branches-workspaces.yarn283
-rw-r--r--yarns/implementations.yarn356
-rw-r--r--yarns/morph.shell-lib153
3 files changed, 792 insertions, 0 deletions
diff --git a/yarns/branches-workspaces.yarn b/yarns/branches-workspaces.yarn
new file mode 100644
index 00000000..bfcb9e66
--- /dev/null
+++ b/yarns/branches-workspaces.yarn
@@ -0,0 +1,283 @@
+Morph black box tests for system branches and workspaces
+========================================================
+
+Morph implements **system branches**, which are checked out and
+manipulated by the user in **workspaces**. See
+FIXME for more information.
+
+Workspace creation
+------------------
+
+The first thing a user needs to do is create a workspace.
+
+ SCENARIO create and initialise a new workspace
+ GIVEN no workspace
+ WHEN morph initialises a workspace
+ THEN an empty workspace exists
+
+The workspace directory may exist, if it is empty.
+
+ SCENARIO initialise an empty workspace directory
+ GIVEN an empty workspace directory
+ WHEN morph initialises a workspace
+ THEN an empty workspace exists
+
+However, the directory must really be empty. It must not be
+an empty, but initialised workspace.
+
+ SCENARIO initialise an existing, empty workspace directory
+ GIVEN no workspace
+ WHEN morph initialises a workspace
+ AND morph attempts to initialise a workspace
+ THEN morph failed
+
+Likewise, if the directory exists, and is non-empty, but isn't an
+existing workspace, initialising it should fail.
+
+ SCENARIO initialise a non-empty workspace directory
+ GIVEN a non-empty workspace directory
+ WHEN morph attempts to initialise a workspace
+ THEN morph failed
+
+Checking out or branching system branches
+-----------------------------------------
+
+Once we have a workspace, we can check out a system branch.
+
+ SCENARIO check out an existing system branch
+ GIVEN a workspace
+ AND a git server
+ WHEN checking out the master system branch
+ THEN the system branch master is checked out
+
+Checking out a system branch should fail, if the branch doesn't exist.
+
+ SCENARIO checking out a system branch that doesn't exist
+ GIVEN a workspace
+ AND a git server
+ WHEN morph attempts to check out system branch foo
+ THEN morph failed
+
+We can, instead, create a new system branch, off master.
+
+ SCENARIO branch off master
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ THEN the system branch foo is checked out
+
+We can also branch off another system branch. However, we need to first
+push the other branch to the git server, since Morph is not smart enough
+to check for that locally.
+
+ SCENARIO branch off non-master
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ AND pushing system branch foo to git server
+ AND creating system branch bar, based on foo
+ THEN the system branch bar is checked out
+
+Query commands in workspaces
+----------------------------
+
+`morph workspace` writes out the fully qualified path to the workspace
+directory, regardless of where the user is. There's a few cases.
+
+ SCENARIO morph workspace works at root of empty workspace
+ GIVEN a workspace
+ WHEN morph reports workspace in .
+ THEN workspace is reported correctly
+
+Also check it in the root of a system branch checkout, and inside
+a git checkout inside that.
+
+ SCENARIO morph workspace works in system branch checkouts
+ GIVEN a workspace
+ AND a git server
+ WHEN checking out the master system branch
+ AND morph reports workspace in master
+ THEN workspace is reported correctly
+
+We leak a little bit of the implementation here, to keep things simple:
+the (mocked) git server the implementation sets up has the `test:morphs`
+repository, which is the system branch root repository.
+
+ WHEN morph reports workspace in master/test:morphs
+ THEN workspace is reported correctly
+
+However, running it outside a workspace should fail.
+
+ SCENARIO morph fails outside workspace
+ GIVEN no workspace
+ WHEN morph attempts to report workspace
+ THEN morph failed
+
+`morph show-system-branch` should report the name of the system
+branch, when run anywhere in the system branch checkout. As a special
+case, if there is only one system branch checkout at or below the
+current working directory, it will find it and report it correctly.
+
+ SCENARIO morph reports system branch
+ GIVEN a workspace
+ AND a git server
+ WHEN checking out the master system branch
+ AND reporting system branch in master
+ THEN system branch is reported as master
+
+ WHEN reporting system branch in master/test:morphs
+ THEN system branch is reported as master
+
+ WHEN reporting system branch in .
+ THEN system branch is reported as master
+
+However, if there's two system branches checked out below the
+current directory, things should fail.
+
+ SCENARIO morph fails to report system branch with two checked out
+ GIVEN a workspace
+ AND a git server
+ WHEN checking out the master system branch
+ AND creating system branch foo
+ AND attempting to report system branch in .
+ THEN morph failed
+
+`morph show-branch-root` reports the URL (possibly aliases) of the
+system branch root repository. It can be run inside a checkout, or
+somewhere outside a checkout, where exactly one checkout exists below.
+
+ SCENARIO morph reports system branch root repository
+ GIVEN a workspace
+ AND a git server
+ WHEN checking out the master system branch
+ AND reporting system branch root repository in master
+ THEN root repository is reported as test:morphs
+
+ WHEN reporting system branch root repository in .
+ THEN root repository is reported as test:morphs
+
+However, it fails if run outside a checkout and there's no system
+branches checked out.
+
+ SCENARIO morph fails to report system branch with two checked out
+ GIVEN a workspace
+ AND a git server
+ WHEN attempting to report system branch root repository in .
+ THEN morph failed
+
+Editing components
+------------------
+
+`morph edit` can edit refs for a stratum only, or it can do that for
+a chunk, and check out the chunk's repository.
+
+First of all, we verify that that when we create a system branch,
+all the refs are unchanged.
+
+ SCENARIO morph branch does not edit refs
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ THEN in branch foo, system test-system refs test-stratum in master
+ AND in branch foo, stratum test-stratum refs test-chunk in master
+
+Then edit the stratum.
+
+ WHEN editing stratum test-stratum in system test-system in branch foo
+ THEN in branch foo, system test-system refs test-stratum in foo
+
+Edit the chunk. We make use of special knowledge here: `test:test-chunk`
+is a chunk repository created in the mocked git server, for testing
+purposes.
+
+ WHEN editing chunk test-chunk in test-stratum in test-system in branch foo
+ THEN in branch foo, system test-system refs test-stratum in foo
+ AND in branch foo, stratum test-stratum refs test-chunk in foo
+ AND edited chunk test:test-chunk has git branch foo
+
+Status of system branch checkout
+--------------------------------
+
+`morph status` shows the status of all git repositories in a
+system branch checkout: only the ones that exist locally, not all the
+repositories referenced in the system branch.
+
+ SCENARIO morph status reports changes correctly
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ THEN morph reports no outstanding changes in foo
+
+ WHEN editing stratum test-stratum in system test-system in branch foo
+ THEN morph reports changes in foo in test:morphs only
+
+ WHEN editing chunk test-chunk in test-stratum in test-system in branch foo
+ THEN morph reports changes in foo in test:morphs only
+
+ WHEN creating file foo in test:test-chunk in branch foo
+ THEN morph reports changes in foo in test:morphs only
+
+ WHEN adding file foo in test:test-chunk in branch foo to git
+ THEN morph reports changes in foo in test:morphs and test:test-chunk
+
+ WHEN committing changes in test:morphs in branch foo
+ THEN morph reports changes in foo in test:test-chunk only
+
+ WHEN committing changes in test:test-chunk in branch foo
+ THEN morph reports no outstanding changes in foo
+
+`morph foreach`
+--------------
+
+`morph foreach` runs a shell command in each of the git repos in a system
+branch checkout.
+
+ SCENARIO morph foreach runs command in each git repo
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ AND editing chunk test-chunk in test-stratum in test-system in branch foo
+ AND running shell command in each repo in foo
+ THEN morph ran command in test:morphs in foo
+ AND morph ran command in test:test-chunk in foo
+
+Explicit petrification
+----------------------
+
+We petrify branches explicitly (though this may later change so that
+`morph branch` does it automatically). To test this, we create a branch,
+petrify it, and verify that every ref looks like a SHA1. We then
+unpetrify and verify that we have all the same refs as before.
+
+ SCENARIO morph petrifies and unpetrifies
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ AND pushing system branch foo to git server
+ AND remembering all refs in foo
+ AND petrifying foo
+ THEN foo is petrified
+ WHEN unpetrifying foo
+ THEN foo refs are as remembered
+
+Tagging system branches
+-----------------------
+
+`morph tag` creates a git tag in the system branch's root repository,
+and a petrified commit the tag refers to. It does not petrify the
+system branch itself, only the tag.
+
+ SCENARIO morph tags a system branch
+ GIVEN a workspace
+ AND a git server
+ WHEN creating system branch foo
+ AND tagging system branch foo as test123
+ THEN morph tag test123 in foo is an annotated git tag
+ AND morph tag test123 in foo refers to a petrified commit
+ AND foo is not petrified
+
+Creating a tag twice should fail.
+
+ WHEN attempting to tag system branch foo as test123
+ THEN morph failed
+
diff --git a/yarns/implementations.yarn b/yarns/implementations.yarn
new file mode 100644
index 00000000..6c0ef6e2
--- /dev/null
+++ b/yarns/implementations.yarn
@@ -0,0 +1,356 @@
+IMPLEMENTS implementations
+==========================
+
+Implementation sections for workspaces
+--------------------------------------
+
+We'll use `$DATADIR/workspace` as the workspace directory that is used.
+
+ IMPLEMENTS GIVEN no workspace
+ true
+
+ IMPLEMENTS GIVEN an empty workspace directory
+ mkdir "$DATADIR/workspace"
+
+ IMPLEMENTS GIVEN a non-empty workspace directory
+ mkdir "$DATADIR/workspace"
+ touch "$DATADIR/workspace/random-file"
+
+We run `morph init` in two different ways: either the simple way,
+letting yarn catch errors, or in a way that catches the error so
+we can test it later in a THEN step.
+
+ IMPLEMENTS WHEN morph initialises a workspace
+ run_morph init "$DATADIR/workspace"
+
+ IMPLEMENTS WHEN morph attempts to initialise a workspace
+ attempt_morph init "$DATADIR/workspace"
+
+ IMPLEMENTS THEN morph failed
+ case $(cat "$DATADIR/morph-exit") in
+ 0) die "Morph should have failed, but didn't. Unexpected success!" ;;
+ esac
+
+We need to check that a workspace creation worked. This requires the
+directory to exist, and its `.morph` subdirectory to exist, and nothing
+else.
+
+ IMPLEMENTS THEN an empty workspace exists
+ is_dir "$DATADIR/workspace"
+ is_dir "$DATADIR/workspace/.morph"
+ assert_equal $(ls -A "$DATADIR/workspace" | wc -l) 1
+
+Tests for things other than `morph init` just want to have a workspace
+created.
+
+ IMPLEMENTS GIVEN a workspace
+ run_morph init "$DATADIR/workspace"
+
+Implementation sections related to a simulated Trove
+----------------------------------------------------
+
+Morph needs access to a Trove, i.e., a git server, in order to do certain
+kinds of stuff. We simulate this by creating a set of git repositories
+locally, which we'll tell Morph to access using `file:` URLs. Specifically,
+we'll create a repository to hold system and stratum morphologies, and
+another to hold a chunk.
+
+ IMPLEMENTS GIVEN a git server
+
+ # Create a directory for all the git repositories.
+ mkdir "$DATADIR/gits"
+
+ # Create a repo for the system and stratum morphologies.
+
+ mkdir "$DATADIR/gits/morphs"
+
+ cat << EOF > "$DATADIR/gits/morphs/test-system.morph"
+ name: test-system
+ kind: system
+ strata:
+ - name: test-stratum
+ repo: test:morphs
+ ref: master
+ morph: test-stratum
+ EOF
+
+ cat << EOF > "$DATADIR/gits/morphs/test-stratum.morph"
+ name: test-stratum
+ kind: stratum
+ chunks:
+ - name: test-chunk
+ repo: test:test-chunk
+ ref: master
+ morph: test-chunk
+ EOF
+
+ run_in "$DATADIR/gits/morphs" git init .
+ run_in "$DATADIR/gits/morphs" git add .
+ run_in "$DATADIR/gits/morphs" git commit -m Initial.
+
+ # Create the chunk repository.
+
+ mkdir "$DATADIR/gits/test-chunk"
+
+ cat << EOF > "$DATADIR/gits/test-chunk/test-chunk.morph"
+ name: test-chunk
+ kind: chunk
+ build-system: dummy
+ EOF
+
+ run_in "$DATADIR/gits/test-chunk" git init .
+ run_in "$DATADIR/gits/test-chunk" git add .
+ run_in "$DATADIR/gits/test-chunk" git commit -m Initial.
+
+ # Create the Morph configuration file so we can access the repos
+ # using test:foo URL aliases.
+
+ cat << EOF > "$DATADIR/morph.conf"
+ [config]
+ repo-alias = test=file://$DATADIR/gits/%s#file://$DATADIR/gits/%s
+ EOF
+
+Implementation sections for system branch operations
+----------------------------------------------------
+
+Checkout out an existing system branch. We parameterise this so the
+same phrase can be used to check out any system branch.
+
+ IMPLEMENTS WHEN checking out the (\S+) system branch
+ cd "$DATADIR/workspace"
+ run_morph checkout test:morphs "$MATCH_1"
+
+Attempt to check out a system branch, and remember if it failed.
+
+ IMPLEMENTS WHEN morph attempts to check out system branch (\S+)
+ cd "$DATADIR/workspace"
+ attempt_morph checkout "$MATCH_1"
+
+We also need to verify that a system branch has been checked out.
+
+ IMPLEMENTS THEN the system branch (\S+) is checked out
+ is_dir "$DATADIR/workspace/$MATCH_1/test:morphs"
+ is_file "$DATADIR/workspace/$MATCH_1/test:morphs/test-system.morph"
+ is_file "$DATADIR/workspace/$MATCH_1/test:morphs/test-stratum.morph"
+
+We can create a new branch, off master.
+
+ IMPLEMENTS WHEN creating system branch (\S+)
+ cd "$DATADIR/workspace"
+ run_morph branch test:morphs "$MATCH_1"
+
+We can create a new branch, off another system branch.
+
+ IMPLEMENTS WHEN creating system branch (\S+), based on (\S+)
+ cd "$DATADIR/workspace"
+ run_morph branch test:morphs "$MATCH_1" "$MATCH_2"
+
+Pushing all changes in a system branch checkout to the git server.
+
+ IMPLEMENTS WHEN pushing system branch (\S+) to git server
+ # FIXME: For now, this is just the morphs checkout.
+ run_in "$DATADIR/workspace/$MATCH_1/test:morphs" git push origin HEAD
+
+Report workspace path.
+
+ IMPLEMENTS WHEN morph reports workspace in (\S+)
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph workspace > "$DATADIR/workspace-reported"
+
+ IMPLEMENTS THEN workspace is reported correctly
+ assert_equal $(cat "$DATADIR/workspace-reported") "$DATADIR/workspace"
+
+ IMPLEMENTS WHEN morph attempts to report workspace
+ cd "$DATADIR"
+ attempt_morph workspace
+
+Report system branch name:
+
+ IMPLEMENTS WHEN reporting system branch in (\S+)
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph show-system-branch > "$DATADIR/system-branch.reported"
+
+ IMPLEMENTS THEN system branch is reported as (.*)
+ echo "$MATCH_1" > "$DATADIR/system-branch.actual"
+ diff -u "$DATADIR/system-branch.actual" "$DATADIR/system-branch.reported"
+
+ IMPLEMENTS WHEN attempting to report system branch in (.*)
+ cd "$DATADIR/workspace/$MATCH_1"
+ attempt_morph show-system-branch
+
+Report system branch root repository.
+
+ IMPLEMENTS WHEN reporting system branch root repository in (.*)
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph show-branch-root > "$DATADIR/branch-root.reported"
+
+ IMPLEMENTS THEN root repository is reported as (.*)
+ echo "$MATCH_1" > "$DATADIR/branch-root.actual"
+ diff -u "$DATADIR/branch-root.actual" "$DATADIR/branch-root.reported"
+
+ IMPLEMENTS WHEN attempting to report system branch root repository in (.*)
+ cd "$DATADIR/workspace/$MATCH_1"
+ attempt_morph show-branch-root
+
+Editing morphologies with `morph edit`.
+
+ IMPLEMENTS THEN in branch (\S+), system (\S+) refs (\S+) in (\S+)
+ "$SRCDIR/scripts/yaml-extract" \
+ "$DATADIR/workspace/$MATCH_1/test:morphs/$MATCH_2.morph" \
+ strata name="$MATCH_3" ref > "$DATADIR/ref.actual"
+ echo "$MATCH_4" > "$DATADIR/ref.wanted"
+ diff -u "$DATADIR/ref.wanted" "$DATADIR/ref.actual"
+
+ IMPLEMENTS THEN in branch (\S+), stratum (\S+) refs (\S+) in (\S+)
+ "$SRCDIR/scripts/yaml-extract" \
+ "$DATADIR/workspace/$MATCH_1/test:morphs/$MATCH_2.morph" \
+ chunks name="$MATCH_3" ref > "$DATADIR/ref.actual"
+ echo "$MATCH_4" > "$DATADIR/ref.wanted"
+ diff -u "$DATADIR/ref.wanted" "$DATADIR/ref.actual"
+
+ IMPLEMENTS WHEN editing stratum (\S+) in system (\S+) in branch (\S+)
+ cd "$DATADIR/workspace/$MATCH_3/test:morphs"
+ run_morph edit "$MATCH_2" "$MATCH_1"
+
+ IMPLEMENTS WHEN editing chunk (\S+) in (\S+) in (\S+) in branch (\S+)
+ cd "$DATADIR/workspace/$MATCH_4/test:morphs"
+ run_morph edit "$MATCH_3" "$MATCH_2" "$MATCH_1"
+
+ IMPLEMENTS THEN edited chunk (\S+) has git branch (\S+)
+ ls -l "$DATADIR/workspace/$MATCH_2"
+ cd "$DATADIR/workspace/$MATCH_2/$MATCH_1"
+ git branch | awk '$1 == "*" { print $2 }' > "$DATADIR/git-branch.actual"
+ echo "$MATCH_2" > "$DATADIR/git-branch.wanted"
+ diff -u "$DATADIR/git-branch.wanted" "$DATADIR/git-branch.actual"
+
+Reporting status of checked out repositories:
+
+ IMPLEMENTS THEN morph reports no outstanding changes in (\S+)
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph status > "$DATADIR/morph.stdout"
+ grep '^No repos have outstanding changes.' "$DATADIR/morph.stdout"
+
+ IMPLEMENTS THEN morph reports changes in (\S+) in (\S+) only
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph status > "$DATADIR/morph.stdout"
+
+ # morph status is expected to produce records like this:
+ # On branch GITBRANCH, root baserock:baserock/morphs
+ # GITREPO: uncommitted changes
+ # We check thet GITREPO matches $MATCH_2.
+
+ awk '/: uncommitted changes$/ { print substr($1,1,length($1)-1) }' \
+ "$DATADIR/morph.stdout" > "$DATADIR/changed.actual"
+ echo "$MATCH_2" > "$DATADIR/changed.wanted"
+ diff -u "$DATADIR/changed.wanted" "$DATADIR/changed.actual"
+
+ IMPLEMENTS THEN morph reports changes in (\S+) in (\S+) and (\S+)
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph status > "$DATADIR/morph.stdout"
+ echo "status morph.stdout:"
+ cat "$DATADIR/morph.stdout"
+ awk '/: uncommitted changes$/ { print substr($1,1,length($1)-1) }' \
+ "$DATADIR/morph.stdout" | sort > "$DATADIR/changed.actual"
+ (echo "$MATCH_2"; echo "$MATCH_3") | sort > "$DATADIR/changed.wanted"
+ diff -u "$DATADIR/changed.wanted" "$DATADIR/changed.actual"
+
+ IMPLEMENTS WHEN creating file (\S+) in (\S+) in branch (\S+)
+ touch "$DATADIR/workspace/$MATCH_3/$MATCH_2/$MATCH_1"
+
+ IMPLEMENTS WHEN adding file (\S+) in (\S+) in branch (\S+) to git
+ cd "$DATADIR/workspace/$MATCH_3/$MATCH_2"
+ git add "$MATCH_1"
+
+ IMPLEMENTS WHEN committing changes in (\S+) in branch (\S+)
+ cd "$DATADIR/workspace/$MATCH_2/$MATCH_1"
+ git commit -a -m test-commit
+
+Running shell command in each checked out repository:
+
+ IMPLEMENTS WHEN running shell command in each repo in (\S+)
+ cd "$DATADIR/workspace/$MATCH_1"
+ run_morph foreach -- pwd > "$DATADIR/morph.stdout"
+
+ IMPLEMENTS THEN morph ran command in (\S+) in (\S+)
+ grep -Fx "$MATCH_1" "$DATADIR/morph.stdout"
+ grep -Fx "$DATADIR/workspace/$MATCH_2/$MATCH_1" "$DATADIR/morph.stdout"
+
+Petrification and unpetrification:
+
+ IMPLEMENTS WHEN remembering all refs in (\S+)
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ list_refs *.morph > "$DATADIR/refs.remembered"
+
+ IMPLEMENTS THEN (\S+) refs are as remembered
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+
+ # FIXME: petrify/unpetrify doesn't work quite right at this time:
+ # petrify can change a ref to a stratum to point at the system
+ # branch, but does it without adding an unpetrify-ref, and so
+ # unpetrify doesn't undo the change. We ignore this bug for the
+ # time being, in order to make the test suite pass. When the
+ # petrification code has been cleaned up to not be so hairy,
+ # we'll fix the test and the code.
+ #
+ # We would like to verify the result like this:
+ #
+ # list_refs *.morph > "$DATADIR/refs.now"
+ # diff -u "$DATADIR/refs.remembered" "$DATADIR/refs.now"
+ #
+ # However, due to the bug, we have to do it in a more complicated
+ # manner.
+
+ list_refs *.morph |
+ while read filename ref
+ do
+ orig=$(awk -v "f=$filename" '$1 == f { print $2 }' \
+ "$DATADIR/refs.remembered")
+ if [ "$orig" != "$ref" ] && [ "$ref" != "$MATCH_1" ]
+ then
+ die "Un-petrified ref: $filename $ref (should be $orig)"
+ fi
+ done
+
+ IMPLEMENTS WHEN petrifying (\S+)
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ run_morph petrify
+
+ IMPLEMENTS WHEN unpetrifying (\S+)
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ run_morph unpetrify
+
+ IMPLEMENTS THEN (\S+) is petrified
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ assert_morphologies_are_petrified "$MATCH_1" *.morph
+
+ IMPLEMENTS THEN (\S+) is not petrified
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ ! assert_morphologies_are_petrified "$MATCH_1" *.morph
+
+Tagging.
+
+ IMPLEMENTS WHEN tagging system branch (\S+) as (\S+)
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ run_morph tag "$MATCH_2" -- -m "testing morph tagging"
+
+ IMPLEMENTS WHEN attempting to tag system branch (\S+) as (\S+)
+ cd "$DATADIR/workspace/$MATCH_1/test:morphs"
+ attempt_morph tag "$MATCH_2" -- -m "testing morph tagging"
+
+ IMPLEMENTS THEN morph tag (\S+) in (\S+) is an annotated git tag
+ cd "$DATADIR/workspace/$MATCH_2/test:morphs"
+ if git show "$MATCH_1" | head -n1 | grep -v '^tag '
+ then
+ die "git tag $MATCH_1 is not an annotated tag"
+ fi
+
+ IMPLEMENTS THEN morph tag (\S+) in (\S+) refers to a petrified commit
+ cd "$DATADIR/workspace/$MATCH_2/test:morphs"
+ git ls-tree "$MATCH_1" |
+ awk '$NF ~ /\.morph$/ { print $NF }' |
+ while read x
+ do
+ git cat-file blob "$MATCH_1:$x" > temptemptemp
+ assert_morphologies_are_petrified "$MATCH_1" temptemptemp
+ done
+
diff --git a/yarns/morph.shell-lib b/yarns/morph.shell-lib
new file mode 100644
index 00000000..f0a9c11b
--- /dev/null
+++ b/yarns/morph.shell-lib
@@ -0,0 +1,153 @@
+# Shell library for Morph yarns.
+#
+# The shell functions in this library are meant to make writing IMPLEMENTS
+# sections for yarn scenario tests easier.
+
+# Copyright (C) 2013 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+# Run Morph from the source tree, ignoring any configuration files.
+# This way the test suite is not affected by any configuration the user
+# or system may have. Instead, we'll use the `$DATADIR/morph.conf` file,
+# which tests can create, if they want to. Unfortunately, currently yarn
+# does not set a $SRCDIR that points at the source tree, so if the test
+# needs to cd away from there, things can break. We work around this
+# by allowing the caller to set $SRCDIR if they want to, and if it isn't
+# set, we default to . (current working directory).
+
+run_morph()
+{
+ "${SRCDIR:-.}"/morph \
+ --no-default-config --config "$DATADIR/morph.conf" "$@"
+}
+
+
+# Sometimes we want to try running morph, but are OK if it fails, we just
+# need to remember that it did.
+
+attempt_morph()
+{
+ if run_morph "$@"
+ then
+ echo 0 > "$DATADIR/morph-exit"
+ else
+ echo "$?" > "$DATADIR/morph-exit"
+ fi
+}
+
+
+# Perl's die() function is often very useful: it prints an error message
+# and terminates the process with a non-zero exit code. Let's have a
+# shell function to do that.
+
+die()
+{
+ echo "ERROR: $@" 1>&2
+ exit 1
+}
+
+
+# Tests often need to check that specific files or directories exist
+# and have the right ownerships etc. Here's some shell functions to
+# test that kind of thing.
+
+is_dir()
+{
+ if [ ! -d "$1" ]
+ then
+ die "Expected $1 to be a directory"
+ fi
+}
+
+is_file()
+{
+ if [ ! -f "$1" ]
+ then
+ die "Expected $1 to be a regular file"
+ fi
+}
+
+
+# General assertions.
+
+assert_equal()
+{
+ case "$1" in
+ "$2") ;;
+ *) die "Expected '$1' and '$2' to be equal" ;;
+ esac
+}
+
+
+# Sometimes it's nice to run a command in a different directory, without
+# having to bother changing the directory before and after the command,
+# or spawning subshells. This function helps with that.
+
+run_in()
+{
+ (cd "$1" && shift && exec "$@")
+}
+
+
+# Extract all refs in all given morphologies. Each ref is reported
+# as filename:ref. The referred-to repository is not listed.
+
+list_refs()
+{
+ awk '/ ref: / { printf "%s %s\n", FILENAME, $NF }' "$@"
+}
+
+
+# Is a ref petrified? Or a specific branch?
+
+is_petrified_or_branch()
+{
+ if echo "$1" |
+ awk -v "branch=$2" '$NF ~ /[0-9a-fA-F]{40}/ || $NF == branch' |
+ grep .
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+# Are named morphologies petrified? Die if not. First arg is the
+# branch that is allowed in addition to SHA1s.
+
+assert_morphologies_are_petrified()
+{
+ local branch="$1"
+ shift
+ list_refs "$@" |
+ while read filename ref
+ do
+ if ! is_petrified_or_branch "$ref" "$branch"
+ then
+ die "Found non-SHA1 ref in $filename: $ref"
+ fi
+ done
+}
+
+
+# Currently, yarn isn't setting $SRCDIR to point at the project source
+# directory. We simulate this here.
+
+if ! env | grep '^SRCDIR=' > /dev/null
+then
+ export SRCDIR="$(pwd)"
+fi