← Back to team overview

kubuntu-council team mailing list archive

[Merge] ~waveform/ubuntu-manual-tests:doctest into ubuntu-manual-tests:master

 

Dave Jones has proposed merging ~waveform/ubuntu-manual-tests:doctest into ubuntu-manual-tests:master.

Requested reviews:
  Ubuntu Testcase Admins (ubuntu-testcase)

For more details, see:
https://code.launchpad.net/~waveform/ubuntu-manual-tests/+git/ubuntu-manual-tests/+merge/410534

A fairly major overhaul of the test_case_gen script. This series of commits:

* Adds a comprehensive test case
* Fixes a few remaining bugs (found largely thanks to the test cases)
* Simplifies the XML structure (remove ut:action and ut:expected and just use straight HTML substitution for these), so it's much closer to the typical HTML output and uses a "proper" URN for the root xml namespace
* Adds a couple of XML definitions for other test cases (basic and non-english installation tests)
* Moves all XML definitions into their own sub-directory with a separate TEMPLATE.xml

The overall diff is large and probably not useful to review at a top level; however, each commit has a (hopefully) descriptive message which should make review of each commit possible.
-- 
Your team Ubuntu Testcase Admins is requested to review the proposed merge of ~waveform/ubuntu-manual-tests:doctest into ubuntu-manual-tests:master.
diff --git a/definitions/TEMPLATE.xml b/definitions/TEMPLATE.xml
new file mode 100644
index 0000000..0613050
--- /dev/null
+++ b/definitions/TEMPLATE.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!--
+Hopefully the layout of this file should be fairly self-explanatory,
+so you may just wish to read the content to get a feel for it, but to
+avoid any ambiguity, here's the long explanation in case it isn't:
+
+The namespace used for all the custom tags is "urn:com:ubuntu:tests"
+which is aliased to the "ut:" shortname at the top, so wherever you see
+a tag with a "ut:" prefix, it's part of the configuration. All unprefixed
+tags are HTML to include in the output.
+
+┌ The outermost element must be "ut:configuration"
+│
+├─┬ Directly under "ut:configuration" there must be one, and only one,
+│ │ "ut:template" element. This contains the HTML used for all generated
+│ │ test cases.
+│ │
+│ └── Within the HTML you must include a *single* "ut:tests" element
+│     which indicates the place within the template to include the
+│     various tests for a given case.
+│
+├─┬ Directly under "ut:configuration", there must be one or more
+│ │ "ut:test" elements. These define the individual tests to be carried
+│ │ out by testers. Each "ut:test" element must have an "id" attribute
+│ │ which uniquely identifies it; the identifier is any arbitrary string
+│ │ and is not directly used in the output.
+│ │
+│ └── Each "ut:test" element must contain the HTML describing the test(s)
+│     to carry out (e.g. "click this button and fill out a form") and the
+│     expected outcome of the test (e.g. "a sheet of paper is printed").
+│
+└─┬ Directly under "ut:configuration", there must be one or more "ut:case"
+  │ elements. Each of these defines a single test-case file to be written
+  │ to the output directory. Each "ut:case" element must have an "id"
+  │ attribute which uniquely identifies it; the identifier will be used
+  │ as the output filename for that case.
+  │
+  ├── Each "ut:case" element must contain one or more "ut:include"
+  │   elements. Each "ut:include" element must have a "ref" attribute
+  │   naming the "id" attribute of the "ut:test" element to include
+  │   in that case. Each test will be included in the output in the
+  │   order specified.
+  │
+  └── Each "ut:case" element may contain zero or more "ut:define"
+      elements. Each "ut:define" element must have a "name" attribute. The
+      content of the "ut:define" element is HTML that will be substituted
+      wherever a "ut:var" element (see below) exists with a matching
+      "name" attribute. The name is an arbitrary string and is not otherwise
+      used in the output.
+
+As the last item above hints, within any section of HTML you may include
+a "ut:var" element. The (mandatory) "name" attribute names the variable
+to substitute in that position. Variables are created with "ut:define"
+elements which may exist directly under "ut:configuration" (in which
+case they apply universally), within a "ut:case" element (in which case
+they apply to all tests included in that case), or within a "ut:include"
+element (in which case they apply to just that included test).
+
+If a variable with the same name is defined in multiple contexts,
+e.g. within a "ut:case" and a "ut:include" within that case, the "inner"
+definition overrides the outer.
+-->
+
+<ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+  <ut:template>
+    <p>This content appears ahead of all the tests included in each produced
+      file. You can place any HTML here that you wish. Additionally, you can
+      include the value of substitution <ut:var name="variables" />.</p>
+    <dl>
+      <!-- The following tag is the placeholder which will be replaced with
+      all the generated test cases. -->
+      <ut:tests />
+    </dl>
+    <p>This content appears after all the tests. A typical footer might be:</p>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a
+      href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  </ut:template>
+
+  <!-- This is a global definition. It can be overridden within ut:case or
+    ut:include elements (see below). -->
+  <ut:define name="variables">variables</ut:define>
+
+  <!-- This is an individual test. The "id" is mandatory, but arbitrary and
+  isn't used in the output anywhere. -->
+  <ut:test id="my-test">
+    <dt>
+      This is a test action. You can place any HTML in here to describe the
+      action the user should take during the test. As with other elements
+      that contain HTML, you can use substitution <ut:var name="variables" />
+      here.
+    </dt>
+    <dd>This is the expected test result.</dd>
+  </ut:test>
+
+  <ut:test id="another-test">
+    <dt>Another example test.</dt>
+    <dd>And another example expected result.</dd>
+    <dt>Yet another test.</dt>
+    <dd>Yes, you can include as many tests as you like within a &lt;ut:test&gt;.</dd>
+  </ut:test>
+
+  <!-- This is a test case. Each case generates one output file named after
+    the (mandatory) "id" attribute. Within here, ut:include elements dictate
+    which tests are included. You may also override the values of ut:define
+    variables within the ut:case, or within an individual ut:include -->
+  <ut:case id="1000_Filename">
+    <ut:include ref="my-test" />
+    <ut:include ref="another-test" />
+  </ut:case>
+
+  <ut:case id="1001_One test">
+    <ut:define name="variables">defines</ut:define>
+    <ut:include ref="my-test" />
+  </ut:case>
+
+  <ut:case id="1002_One other test">
+    <ut:include ref="my-test">
+      <ut:define name="variables">defines</ut:define>
+    </ut:include>
+  </ut:case>
+</ut:configuration>
diff --git a/definitions/basic_installation.xml b/definitions/basic_installation.xml
new file mode 100644
index 0000000..413dd32
--- /dev/null
+++ b/definitions/basic_installation.xml
@@ -0,0 +1,129 @@
+<ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+  <ut:template>
+    <p><em>Proceed in your native language if you wish. Instructions
+      will remain in English.</em></p>
+    <dl>
+      <ut:tests />
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a
+      href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  </ut:template>
+
+  <ut:test id="start">
+    <dt>Boot up the image</dt>
+        <dd>The system boots properly and loads the installer displaying the Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the release notes hyperlink to confirm that a browser launches and you are taken to the release notes web page.</dt>
+    <dt>Click on the Install FAMILY button</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Updates and other software' screen is displayed</dd>
+    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+  </ut:test>
+
+  <ut:test id="entire-disk">
+    <dt>Select Erase disk and install FAMILY</dt>
+        <dd>Installation screen expands to include encryption and LVM options</dd>
+    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
+        <dd>Write changes dialogue appears</dd>
+    <dt>Click continue</dt>
+        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
+    <dt>If there is only one hard disk, skip to step 12 (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' page, select a drive in the 'Select drive' list.</dt>
+        <dd>Verify that the selected drive corresponds to the drive on the chart</dd>
+    <dt>Verify that the full drive space is allocated</dt>
+        <dd>Full drive space is allocated for installation</dd>
+    <dt>Click on the Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  </ut:test>
+
+  <ut:test id="auto-resize">
+    <dt>Select Install FAMILY XX.XX alongside SYSTEM YY </dt>
+        <dd>(SYSTEM YY is the name of the system already installed on disk (FAMILY 12.04, Windows 7, ...)</dd>
+    <dt>Click on Continue</dt>
+        <dd>The Screen Install FAMILY XX.XX alongside SYSTEM YY appears</dd>
+    <dt>Note the drive selected on the Select drive list and the bar state</dt>
+    <strong>If the target drive has a free partition with sufficient freespace, install will proceed without further partitioning intervention</strong>
+        <dd> The drive corresponds to the drive on the chart (e.g /dev/sda) and the bar is divided</dd>
+    <dt>Move the greyslider bar that separates the blue sections as appropriate</dt>
+        <dd>The slider bar can be set as appropriate</dd>
+    <dt>Click on the Install Now button</dt>
+    <dt> A dialog will appear  asking if you want to write the changes to disk. Select Continue.</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  </ut:test>
+
+  <ut:test id="manual-partitions">
+    <dt>Select Something Else</dt>
+        <dd>A screen showing the current hard disks and partition layouts is displayed</dd>
+    <dt>Select the drive you wish to partition and use the Add '+', Change 'Change...', and Delete '-' buttons to create your desired scheme</dt>
+        <dd>The screen updates showing your desired partitions and mount points</dd>
+    <dt>Once you have your required partitioning scheme laid out, click on the Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  </ut:test>
+
+  <ut:test id="finish">
+    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
+        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password <em>admin can not be used - it is a dedicated Linux User</em></dt>
+        <dd>Name, username and password are accepted. Automatic login option is shown</dd>
+        <dd>Continue button becomes available</dd>
+    <dt>Press continue</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The slideshow is entirely in your language</dd> 
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine is rebooted</dd>
+  </ut:test>
+
+  <ut:test id="reboot-ubuntu">
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY showing username selected</dd>
+  </ut:test>
+
+  <ut:test id="reboot-other">
+    <dt>Allow the machine to reboot, select the first option at the grub menu</dt>
+        <dd>Your new installation boots</dd>
+    <dt>Reboot machine, select the previous installed system</dt>
+    <dd>Previously installed system boots and operates as expected</dd>
+  </ut:test>
+
+  <ut:case id="1300_Install (entire disk)">
+    <ut:include ref="start" />
+    <ut:include ref="entire-disk" />
+    <ut:include ref="finish" />
+    <ut:include ref="reboot-ubuntu" />
+  </ut:case>
+
+  <ut:case id="1301_Install (auto-resize)">
+    <ut:include ref="start" />
+    <ut:include ref="auto-resize" />
+    <ut:include ref="finish" />
+    <ut:include ref="reboot-other" />
+  </ut:case>
+
+  <ut:case id="1302_Install (manual partitioning)">
+    <ut:include ref="start" />
+    <ut:include ref="manual-partitions" />
+    <ut:include ref="finish" />
+    <ut:include ref="reboot-ubuntu" />
+  </ut:case>
+</ut:configuration>
diff --git a/definitions/non_english_installation.xml b/definitions/non_english_installation.xml
new file mode 100644
index 0000000..10bf164
--- /dev/null
+++ b/definitions/non_english_installation.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!-- See TEMPLATE.xml for an explanation of this file layout -->
+
+<ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+  <ut:template>
+    <p>The goal of this test case is to check that localization support is
+      functional during the installation, <ut:var name="preamble" /></p>
+    <dl>
+      <ut:tests />
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a
+      href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  </ut:template>
+
+  <ut:test id="start">
+    <dt>Boot up the image. When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
+        <dd>Language list will appear</dd>
+    <dt>Use arrow keys to select language and press Enter</dt>
+        <dd>Language list will close.</dd>
+        <dd>The installer is localized from this screen onwards</dd>
+    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
+        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the Install FAMILY icon</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
+    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
+        <dd>The 'Erase disk and install FAMILY' radio button is selected</dd>
+    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
+        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
+    <dt>If there is only one hard disk, skip two steps to selecting the timezone (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
+        <dd>Selected drive is displayed on the chart</dd>
+    <dt>Verify that the full drive space is allocated</dt>
+        <dd>Full drive space is allocated for installation</dd>
+    <dt>Click on Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  </ut:test>
+
+  <ut:test id="auto-timezone">
+    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
+        <dd>The timezone and city displayed match your timezone and a city from your area</dd>
+  </ut:test>
+
+  <ut:test id="user-setup">
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
+        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The installer's slideshow slides are completely localized</dd>
+  </ut:test>
+
+  <ut:test id="disconnect-net">
+    <dt>When the installer is installing the language packs, disconnect the Internet</dt>
+  </ut:test>
+
+  <ut:test id="locale-net">
+    <dt>Verify that your system is localized</dt>
+        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
+        <dd>The calendar shows the regional settings correctly</dd>
+  </ut:test>
+
+  <ut:test id="locale-no-net">
+    <dt>Verify that your system is localized</dt>
+        <dd>If you aren't using Bengali (bn), German (de), English (en), Spanish (es), French(fr), Portuguese (pt) or Xhosa (xh) a pop up appears showing saying your language support is not complete</dd>
+        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
+        <dd>If you are using one of the above languages the calendar shows the regional settings correctly</dd>
+  </ut:test>
+
+  <ut:test id="finish">
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine has been rebooted</dd>
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY</dd>
+  </ut:test>
+
+  <ut:case id="1311_Non-English Installation Full Network Support">
+    <ut:define name="preamble">
+      and that language packs are downloaded and installed correctly
+      from the Internet.
+    </ut:define>
+
+    <ut:include ref="start" />
+    <ut:include ref="auto-timezone" />
+    <ut:include ref="user-setup" />
+    <ut:include ref="locale-net" />
+    <ut:include ref="finish"/>
+  </ut:case>
+
+  <ut:case id="1312_Non-English Installation No Network">
+    <ut:define name="preamble">
+      and that language packs are installed correctly for those languages
+      in the ISO, and that an informational message is shown prompting
+      users to connect to the Internet to fully install language support
+      for those languages not included.
+    </ut:define>
+
+    <ut:include ref="start" />
+    <ut:include ref="user-setup" />
+    <ut:include ref="locale-no-net" />
+    <ut:include ref="finish"/>
+  </ut:case>
+
+  <ut:case id="1313_Installation Broken Internet">
+    <ut:define name="preamble">
+      and that language packs are installed correctly for those languages
+      in the ISO. During the lang-packs installation, Internet breaks, and the
+      installer handles it gracefully. After the installation is complete and
+      the new system rebooted an informational message is shown prompting users
+      to connect to the Internet to fully install language support.
+    </ut:define>
+
+    <ut:include ref="start" />
+    <ut:include ref="disconnect-net" />
+    <ut:include ref="user-setup" />
+    <ut:include ref="locale-no-net" />
+    <ut:include ref="finish"/>
+  </ut:case>
+</ut:configuration>
diff --git a/tools/pi_image_cases.xml b/definitions/pi_image_cases.xml
similarity index 55%
rename from tools/pi_image_cases.xml
rename to definitions/pi_image_cases.xml
index b3717ee..32ab3e7 100644
--- a/tools/pi_image_cases.xml
+++ b/definitions/pi_image_cases.xml
@@ -1,70 +1,8 @@
 <?xml version="1.0" encoding="utf-8" ?>
 
-<!--
-Hopefully the layout of this file should be fairly self-explanatory,
-so you may just wish to read the content to get a feel for it, but to
-avoid any ambiguity, here's the long explanation in case it isn't:
+<!-- See TEMPLATE.xml for an explanation of this file layout -->
 
-The namespace used for all the custom tags is "com.ubuntu.tests" which
-is aliased to the "ut:" shortname at the top, so wherever you see a tag
-with a "ut:" prefix, it's part of the configuration. All unprefixed tags
-are HTML to include in the output.
-
-+ The outermost element must be "ut:configuration"
-|
-|-+ Directly under "ut:configuration" there must be one, and only one,
-|   "ut:template" element. This contains the HTML used for all generated
-|   test cases. Within the HTML you must include a *single* "ut:tests"
-|   element which indicates the place within the template to include
-|   the various tests for a given case.
-|
-|-+ Directly under "ut:configuration", there must be one or more
-| | "ut:test" elements. These define the individual tests to be carried
-| | out by testers. Each "ut:test" element must have an "id" attribute
-| | which uniquely identifies it; the identifier is any arbitrary string
-| | and is not directly used in the output.
-| |
-| |-+ Each "ut:test" element must contain a single "ut:action" element,
-| |   which contains the HTML describing the test to carry out
-| |   (e.g. "click this button and fill out a form")
-| |
-| \-+ Each "ut:test" element must also contain a single "ut:expected"
-|     element, which contains the HTML describing the expected outcome
-|     of the test (e.g. "a sheet of paper is printed").
-|
-\-+ Directly under "ut:configuration", there must be one or more "ut:case"
-  | elements. Each of these defines a single test-case file to be written
-  | to the output directory. Each "ut:case" element must have an "id"
-  | attribute which uniquely identifies it; the identifier will be used
-  | as the output filename for that case.
-  |
-  |-+ Each "ut:case" element must contain one or more "ut:include"
-  |   elements. Each "ut:include" element must have a "ref" attribute
-  |   naming the "id" attribute of the "ut:test" element to include
-  |   in that case. Each test will be included in the output in the
-  |   order specified.
-  |
-  \-+ Each "ut:case" element may contain zero or more "ut:define"
-      elements. Each "ut:define" element must have a "name" attribute. The
-      content of the "ut:define" element is HTML that will be substituted
-      wherever a "ut:var" element (see below) exists with a matching
-      "name" attribute. The name is an arbitrary string and is not otherwise
-      used in the output.
-
-As the last item above hints, within any section of HTML you may include
-a "ut:var" element. The (mandatory) "name" attribute names the variable
-to substitute in that position. Variables are created with "ut:define"
-elements which may exist directly under "ut:configuration" (in which
-case they apply universally), within a "ut:case" element (in which case
-they apply to all tests included in that case), or within a "ut:include"
-element (in which case they apply to just that included test).
-
-If a variable with the same name is defined in multiple contexts,
-e.g. within a "ut:case" and a "ut:include" within that case, the "inner"
-definition overrides the outer.
--->
-
-<ut:configuration xmlns:ut="com.ubuntu.tests">
+<ut:configuration xmlns:ut="urn:com:ubuntu:tests">
   <ut:template>
     <p>This test case is to be carried out on a <ut:var name="model" />.</p>
     <p>Follow the installation steps at <a
@@ -87,59 +25,59 @@ definition overrides the outer.
   <ut:define name="post-install"></ut:define>
 
   <ut:test id="uboot-video">
-    <ut:action>
+    <dt>
       Immediately after the "rainbow" splash screen, U-Boot starts. Press a key
       to interrupt the sequence, then type "boot" to continue it.
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       Check that the U-Boot prompt (including a two-second wait) appears on
       the primary monitor connected to the Pi, that the keyboard will interrupt
       the sequence, and that boot successfully concludes after resumption.
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="uboot-serial">
-    <ut:action>
+    <dt>
       During the boot sequence, U-Boot starts interacting via serial (GPIO14
       and GPIO15, pins 8 and 10 respectively, on the 40-pin GPIO header). Press
       a key to interrupt the sequence, then type "boot" to continue it.
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       Check that the U-Boot prompt (including a two-second wait) appears on
       the attached serial console, that the keyboard will interrupt
       the sequence, and that boot successfully concludes after resumption.
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="flash-kernel">
-    <ut:action>
+    <dt>
       Run <code>sudo flash-kernel</code>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       Exit code is clean (0) and no error messages are reported
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="reboot">
-    <ut:action>
+    <dt>
       Run <code>sudo reboot</code>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       System reboots successfully to a login prompt
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="shutdown">
-    <ut:action>
+    <dt>
       Run <code>sudo shutdown -h now</code>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       System shuts down in a reasonable time (less than a minute)
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="usb-file-transfer">
-    <ut:action>
+    <dt>
       Perform a large (300-600MB) file copy to USB storage
       <ul>
         <li>Generate a large (500MB) file: <code>dd if=/dev/urandom of=rubbish
@@ -158,44 +96,44 @@ definition overrides the outer.
         <li>Compare the copied file to that on the stick: <code>cmp rubbish
           /mnt/stick/rubbish</code></li>
       </ul>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       <code>cmp</code> returns 0 and outputs nothing indicating the files are
       identical
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="usb-keyboard">
-    <ut:action>
+    <dt>
       Connect a USB keyboard to one of the <ut:var name="usb" /> ports
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       Verify that keys typed on the keyboard appear on the console
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="ram-free">
-    <ut:action>
+    <dt>
       Check output of <code>free -h</code>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       Reported &quot;Mem&quot; under &quot;total&quot; is consistent with a
       <ut:var name="model" />. It should be in the region of <ut:var name="mem"
       />.
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="dual-monitor">
-    <ut:action>
+    <dt>
       Boot with a monitor attached to both HDMI ports
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       Verify that you see output on both monitors
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="ethernet">
-    <ut:action>
+    <dt>
       Check auto-configuration of ethernet
       <ul>
         <li>Run <code>ip addr</code></li>
@@ -204,15 +142,15 @@ definition overrides the outer.
         <li>Check <code>ping google.com</code> successfully pings a few times
           (<tt>Ctrl+C</tt> to cancel)</li>
       </ul>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       The &quot;<ut:var name="intf" />&quot; interface should have a DHCP
       assigned IP address and you should be able to ping google.com
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="wifi">
-    <ut:action>
+    <dt>
       Configure wifi via netplan
       <ul>
         <li>Place the following in <code>/etc/netplan/wifi.yaml</code>
@@ -235,15 +173,15 @@ definition overrides the outer.
         <li>Check <code>ping google.com</code> successfully pings a few times
           (<tt>Ctrl+C</tt> to cancel)</li>
       </ul>
-    </ut:action>
-    <ut:expected>
+    </dt>
+    <dd>
       The &quot;<ut:var name="intf" />&quot; interface should have a DHCP
       assigned IP address and you should be able to ping google.com
-    </ut:expected>
+    </dd>
   </ut:test>
 
   <ut:test id="audio">
-    <ut:action>
+    <dt>
       With <ut:var name="device" />, and an available MP3 file:
       <ul>
         <li>Install mpg321 and amixer with <code>sudo apt install mpg321
@@ -265,8 +203,8 @@ definition overrides the outer.
           (<tt>J</tt> for down, <tt>K</tt> for up) before exiting
           (<tt>Esc</tt>) and retrying playback</li>
       </ul>
-    </ut:action>
-    <ut:expected>Audio can be heard through the device</ut:expected>
+    </dt>
+    <dd>Audio can be heard through the device</dd>
   </ut:test>
 
   <ut:case id="1711_RaspberryPi 4 2GB Post-install">
diff --git a/testcases/image/1300_Install (entire disk) b/testcases/image/1300_Install (entire disk)
index 53652f7..e01f907 100755
--- a/testcases/image/1300_Install (entire disk)
+++ b/testcases/image/1300_Install (entire disk)
@@ -1,55 +1,75 @@
-<em>Proceed in your native language if you wish. Instructions will remain in English</em>
-
-<dl>
-    <dt>Boot up the image</dt>
-        <dd>The system boots properly and loads the installer displaying the Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
-    <dt>Click on the release notes hyperlink to confirm that a browser launches and you are taken to the release notes web page.</dt>
-    <dt>Click on the Install FAMILY button</dt>
-        <dd>The 'Keyboard Layout' screen appears</dd>
-        <dd>The proposed keyboard corresponds with your keyboard</dd>
-	<dt>Select your keyboard layout and click on continue</dt>
-        <dd>The 'Updates and other software' screen is displayed</dd>
-    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
-        <dd>Available options should represent the state of your system accurately</dd>
-        <dd>
-            <ul>
-                <li>(If network is available) Download updates while installing FAMILY</li>
-                <li>(If on a 'laptop') Is plugged to a power source</li>
-                <li>Install third-party software ... option available</li>
-            </ul>
-        </dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Installation type' screen is displayed</dd>
-    <dt>Select Erase disk and install FAMILY</dt>
-        <dd>Installation screen expands to include encryption and LVM options</dd>
-    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
-        <dd>Write changes dialogue appears</dd>
-    <dt>Click continue</dt>
-        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
-    <dt>If there is only one hard disk, skip to step 12 (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' page, select a drive in the 'Select drive' list.</dt>
-        <dd>Verify that the selected drive corresponds to the drive on the chart</dd>
-    <dt>Verify that the full drive space is allocated</dt>
-        <dd>Full drive space is allocated for installation</dd>
-    <dt>Click on the Install Now button</dt>
-        <dd>The 'Where are you?' screen is displayed</dd>
-    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
-        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
-    <dt>Select your timezone, and click on the continue button</dt>
-        <dd>The 'Who are you?' screen appears</dd>
-    <dt>Input your initial user details and password <em>admin can not be used - it is a dedicated Linux User</em></dt>
-        <dd>Name, username and password are accepted. Automatic login option is shown</dd>
-        <dd>Continue button becomes available</dd>
-    <dt>Press continue</dt>
-        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
-        <dd>The slideshow is entirely in your language</dd> 
-    <dt>Wait for the installer to finish</dt>
-        <dd>An 'Installation Complete' dialog appears</dd>
-    <dt>Click the Restart now button</dt>
-        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
-    <dt>Remove the disc and press enter</dt>
-        <dd>The machine is rebooted</dd>
-    <dt>Allow the machine to reboot</dt>
-        <dd>The system boots properly and loads into FAMILY showing username selected at step 15</dd>
-</dl>
-<strong>If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
-    If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result.</strong>
+<!-- Please do not edit this file directly; it was generated with the
+     tools/test_case_gen script using the following configuration as input:
+     definitions/basic_installation.xml
+-->
+
+    <p><em>Proceed in your native language if you wish. Instructions
+      will remain in English.</em></p>
+    <dl>
+      
+    
+    <dt>Boot up the image</dt>
+        <dd>The system boots properly and loads the installer displaying the Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the release notes hyperlink to confirm that a browser launches and you are taken to the release notes web page.</dt>
+    <dt>Click on the Install FAMILY button</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Updates and other software' screen is displayed</dd>
+    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+  
+    
+    <dt>Select Erase disk and install FAMILY</dt>
+        <dd>Installation screen expands to include encryption and LVM options</dd>
+    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
+        <dd>Write changes dialogue appears</dd>
+    <dt>Click continue</dt>
+        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
+    <dt>If there is only one hard disk, skip to step 12 (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' page, select a drive in the 'Select drive' list.</dt>
+        <dd>Verify that the selected drive corresponds to the drive on the chart</dd>
+    <dt>Verify that the full drive space is allocated</dt>
+        <dd>Full drive space is allocated for installation</dd>
+    <dt>Click on the Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  
+    
+    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
+        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password <em>admin can not be used - it is a dedicated Linux User</em></dt>
+        <dd>Name, username and password are accepted. Automatic login option is shown</dd>
+        <dd>Continue button becomes available</dd>
+    <dt>Press continue</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The slideshow is entirely in your language</dd> 
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine is rebooted</dd>
+  
+    
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY showing username selected</dd>
+  
+  
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  
\ No newline at end of file
diff --git a/testcases/image/1301_Install (auto-resize) b/testcases/image/1301_Install (auto-resize)
index ff04641..74eb5da 100755
--- a/testcases/image/1301_Install (auto-resize)
+++ b/testcases/image/1301_Install (auto-resize)
@@ -1,57 +1,77 @@
-<strong> Check that an existing system is installed and that sufficient disk space is available for an additional system to be installed during the test</strong>
-
-<em>Proceed in your native language if you wish. Instructions will remain in English</em>
-
-<dl>
-    <dt>Boot up the image</dt>
-        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
-    <dt>Click on the Install FAMILY icon</dt>
-        <dd>The 'Keyboard Layout' screen appears</dd>
-        <dd>The proposed keyboard corresponds with your keyboard</dd>
-	<dt>Select your keyboard layout and click on continue</dt>
-        <dd>The 'Updates and other software' screen is displayed</dd>
-    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
-        <dd>Available options should represent the state of your system accurately</dd>
-        <dd>
-            <ul>
-                <li>(If network is available) Download updates while installing FAMILY</li>
-                <li>(If on a 'laptop') Is plugged to a power source</li>
-                <li>Install third-party software ... option available</li>
-            </ul>
-        </dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Installation type' screen is displayed</dd>
-    <dt>Select Install FAMILY XX.XX alongside SYSTEM YY </dt>
-        <dd>(SYSTEM YY is the name of the system already installed on disk (FAMILY 12.04, Windows 7, ...)</dd>
-    <dt>Click on Continue</dt>
-        <dd>The Screen Install FAMILY XX.XX alongside SYSTEM YY appears</dd>
-    <dt>Note the drive selected on the Select drive list and the bar state</dt>
-    <strong>If the target drive has a free partition with sufficient freespace, install will proceed without further partitioning intervention</strong>
-        <dd> The drive corresponds to the drive on the chart (e.g /dev/sda) and the bar is divided</dd>
-    <dt>Move the greyslider bar that separates the blue sections as appropriate</dt>
-        <dd>The slider bar can be set as appropriate</dd>
-    <dt>Click on the Install Now button</dt>
-    <dt> A dialog will appear  asking if you want to write the changes to disk. Select Continue.</dt>
-        <dd>The 'Where are you?' screen is displayed</dd>
-    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
-        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
-    <dt>Select your timezone, and click on the continue button</dt>
-        <dd>The 'Who are you?' screen appears</dd>
-    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
-        <dd>Name, username and password are accepted. Additionally, the continue button is enabled and a radio button saying require my password to login is selected, a radio button to log in automatically is not selected and a checkbox for encrypt my home folder is not selected.</dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
-        <dd>The slideshow is entirely in your language</dd> 
-    <dt>Wait for the installer to finish</dt>
-        <dd>An 'Installation Complete' dialog appears</dd>
-    <dt>Click the Restart now button</dt>
-        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
-    <dt>Remove the disc and press enter</dt>
-        <dd>The machine is rebooted</dd>
-    <dt>Allow the machine to reboot, select the first option at the grub menu</dt>
-        <dd>Your new installation boots</dd>
-    <dt>Reboot machine, select the previous installed system</dt>
-	<dd>Previously installed system boots and operates as expected</dd>
-</dl>
-<strong>If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
-    If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result.</strong>
+<!-- Please do not edit this file directly; it was generated with the
+     tools/test_case_gen script using the following configuration as input:
+     definitions/basic_installation.xml
+-->
+
+    <p><em>Proceed in your native language if you wish. Instructions
+      will remain in English.</em></p>
+    <dl>
+      
+    
+    <dt>Boot up the image</dt>
+        <dd>The system boots properly and loads the installer displaying the Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the release notes hyperlink to confirm that a browser launches and you are taken to the release notes web page.</dt>
+    <dt>Click on the Install FAMILY button</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Updates and other software' screen is displayed</dd>
+    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+  
+    
+    <dt>Select Install FAMILY XX.XX alongside SYSTEM YY </dt>
+        <dd>(SYSTEM YY is the name of the system already installed on disk (FAMILY 12.04, Windows 7, ...)</dd>
+    <dt>Click on Continue</dt>
+        <dd>The Screen Install FAMILY XX.XX alongside SYSTEM YY appears</dd>
+    <dt>Note the drive selected on the Select drive list and the bar state</dt>
+    <strong>If the target drive has a free partition with sufficient freespace, install will proceed without further partitioning intervention</strong>
+        <dd> The drive corresponds to the drive on the chart (e.g /dev/sda) and the bar is divided</dd>
+    <dt>Move the greyslider bar that separates the blue sections as appropriate</dt>
+        <dd>The slider bar can be set as appropriate</dd>
+    <dt>Click on the Install Now button</dt>
+    <dt> A dialog will appear  asking if you want to write the changes to disk. Select Continue.</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  
+    
+    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
+        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password <em>admin can not be used - it is a dedicated Linux User</em></dt>
+        <dd>Name, username and password are accepted. Automatic login option is shown</dd>
+        <dd>Continue button becomes available</dd>
+    <dt>Press continue</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The slideshow is entirely in your language</dd> 
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine is rebooted</dd>
+  
+    
+    <dt>Allow the machine to reboot, select the first option at the grub menu</dt>
+        <dd>Your new installation boots</dd>
+    <dt>Reboot machine, select the previous installed system</dt>
+    <dd>Previously installed system boots and operates as expected</dd>
+  
+  
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  
\ No newline at end of file
diff --git a/testcases/image/1302_Install (manual partitioning) b/testcases/image/1302_Install (manual partitioning)
index a953871..a53f57a 100755
--- a/testcases/image/1302_Install (manual partitioning)
+++ b/testcases/image/1302_Install (manual partitioning)
@@ -1,47 +1,69 @@
-<em>Proceed in your native language if you wish. Instructions will remain in English</em>
-
-<dl>
-    <dt>Boot up the image</dt>
-        <dd>The system boots properly and loads the installer displaying the Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
-    <dt>Click on the Install FAMILY button</dt>
-        <dd>The 'Keyboard Layout' screen appears</dd>
-        <dd>The proposed keyboard corresponds with your keyboard</dd>
-	<dt>Select your keyboard layout and click on continue</dt>
-        <dd>The 'Updates and other software' screen is displayed</dd>
-    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
-        <dd>Available options should represent the state of your system accurately</dd>
-        <dd>
-            <ul>
-                <li>(If network is available) Download updates while installing FAMILY</li>
-                <li>(If on a 'laptop') Is plugged to a power source</li>
-                <li>Install third-party software ... option available</li>
-            </ul>
-        </dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Installation type' screen is displayed</dd>
-    <dt>Select Something Else</dt>
-        <dd>A screen showing the current hard disks and partition layouts is displayed</dd>
-    <dt>Select the drive you wish to partition and use the Add '+', Change 'Change...', and Delete '-' buttons to create your desired scheme</dt>
-        <dd>The screen updates showing your desired partitions and mount points</dd>
-    <dt>Once you have your required partitioning scheme laid out, click on the Install Now button</dt>
-        <dd>The 'Where are you?' screen is displayed</dd>
-    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
-        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
-    <dt>Select your timezone, and click on the continue button</dt>
-        <dd>The 'Who are you?' screen appears</dd>
-    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
-        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
-        <dd>The slideshow is entirely in your language</dd> 
-    <dt>Wait for the installer to finish</dt>
-        <dd>An 'Installation Complete' dialog appears</dd>
-    <dt>Click the Restart now button</dt>
-        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
-    <dt>Remove the disc and press enter</dt>
-        <dd>The machine is rebooted</dd>
-    <dt>Allow the machine to reboot</dt>
-        <dd>The system boots properly and loads into FAMILY</dd>
-</dl>
-<strong>If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
-    If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result.</strong>
+<!-- Please do not edit this file directly; it was generated with the
+     tools/test_case_gen script using the following configuration as input:
+     definitions/basic_installation.xml
+-->
+
+    <p><em>Proceed in your native language if you wish. Instructions
+      will remain in English.</em></p>
+    <dl>
+      
+    
+    <dt>Boot up the image</dt>
+        <dd>The system boots properly and loads the installer displaying the Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the release notes hyperlink to confirm that a browser launches and you are taken to the release notes web page.</dt>
+    <dt>Click on the Install FAMILY button</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Updates and other software' screen is displayed</dd>
+    <dt>On the screen 'Updates and other software', note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+  
+    
+    <dt>Select Something Else</dt>
+        <dd>A screen showing the current hard disks and partition layouts is displayed</dd>
+    <dt>Select the drive you wish to partition and use the Add '+', Change 'Change...', and Delete '-' buttons to create your desired scheme</dt>
+        <dd>The screen updates showing your desired partitions and mount points</dd>
+    <dt>Once you have your required partitioning scheme laid out, click on the Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  
+    
+    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
+        <dd>The timezone and city displayed match your timezone and the main city from your area</dd>
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password <em>admin can not be used - it is a dedicated Linux User</em></dt>
+        <dd>Name, username and password are accepted. Automatic login option is shown</dd>
+        <dd>Continue button becomes available</dd>
+    <dt>Press continue</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The slideshow is entirely in your language</dd> 
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine is rebooted</dd>
+  
+    
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY showing username selected</dd>
+  
+  
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  
\ No newline at end of file
diff --git a/testcases/image/1311_Non-English Installation Full Network Support b/testcases/image/1311_Non-English Installation Full Network Support
index 965afbd..75afbdc 100755
--- a/testcases/image/1311_Non-English Installation Full Network Support
+++ b/testcases/image/1311_Non-English Installation Full Network Support
@@ -1,58 +1,84 @@
-The goal of this test case is to check that localization support is functional during the installation and that language packs are downloaded and installed correctly from the Internet.
-<dl>
-    <dt>Boot up the image.  When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
-        <dd>Language list will appear</dd>
-    <dt>Use arrow keys to select language and press Enter</dt>
-        <dd>Language list will close.</dd>
-        <dd>The installer is localized from this screen onwards</dd>
-    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
-        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
-    <dt>Click on the Install FAMILY icon</dt>
-        <dd>The 'Keyboard Layout' screen appears</dd>
-        <dd>The proposed keyboard corresponds with your keyboard</dd>
-	<dt>Select your keyboard layout and click on continue</dt>
-        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
-    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
-        <dd>Available options should represent the state of your system accurately</dd>
-        <dd>
-            <ul>
-                <li>(If network is available) Download updates while installing FAMILY</li>
-                <li>(If on a 'laptop') Is plugged to a power source</li>
-                <li>Install third-party software ... option available</li>
-            </ul>
-        </dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Installation type' screen is displayed</dd>
-    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
-        <dd>The 'Erase disk and install FAMILY' radio button is selected</dd>
-    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
-        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
-    <dt>If there is only one hard disk, skip to step 10 (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
-        <dd>Selected drive is displayed on the chart</dd>
-    <dt>Verify that the full drive space is allocated</dt>
-        <dd>Full drive space is allocated for installation</dd>
-    <dt>Click on Install Now button</dt>
-        <dd>The 'Where are you?' screen is displayed</dd>
-    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
-        <dd>The timezone and city displayed match your timezone and a city from your area</dd>
-    <dt>Select your timezone, and click on the continue button</dt>
-        <dd>The 'Who are you?' screen appears</dd>
-    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
-        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
-        <dd>The installer's slideshow slides are completely localized</dd>
-    <dt>Wait for the installer to finish</dt>
-        <dd>An 'Installation Complete' dialog appears</dd>
-    <dt>Click the Restart now button</dt>
-        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
-    <dt>Remove the disc and press enter</dt>
-        <dd>The machine has been rebooted</dd>
-    <dt>Allow the machine to reboot</dt>
-        <dd>The system boots properly and loads into FAMILY</dd>
-    <dt>Verify that your system is localized</dt>
-        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
-        <dd>The calendar shows the regional settings correctly</dd>
-</dl>
-<strong>If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
-    If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result.</strong>
+<!-- Please do not edit this file directly; it was generated with the
+     tools/test_case_gen script using the following configuration as input:
+     tools/non_english_installation.xml
+-->
+
+    <p>The goal of this test case is to check that localization support is
+      functional during the installation, 
+      and that language packs are downloaded and installed correctly
+      from the Internet.
+    </p>
+    <dl>
+      
+    
+    <dt>Boot up the image. When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
+        <dd>Language list will appear</dd>
+    <dt>Use arrow keys to select language and press Enter</dt>
+        <dd>Language list will close.</dd>
+        <dd>The installer is localized from this screen onwards</dd>
+    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
+        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the Install FAMILY icon</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
+    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
+        <dd>The 'Erase disk and install FAMILY' radio button is selected</dd>
+    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
+        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
+    <dt>If there is only one hard disk, skip two steps to selecting the timezone (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
+        <dd>Selected drive is displayed on the chart</dd>
+    <dt>Verify that the full drive space is allocated</dt>
+        <dd>Full drive space is allocated for installation</dd>
+    <dt>Click on Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  
+    
+    <dt>If your system is connected to the network, note the preselected timezone correspond with your timezone and the city indicated in the text box </dt>
+        <dd>The timezone and city displayed match your timezone and a city from your area</dd>
+  
+    
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
+        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The installer's slideshow slides are completely localized</dd>
+  
+    
+    <dt>Verify that your system is localized</dt>
+        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
+        <dd>The calendar shows the regional settings correctly</dd>
+  
+    
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine has been rebooted</dd>
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY</dd>
+  
+  
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  
\ No newline at end of file
diff --git a/testcases/image/1312_Non-English Installation No Network b/testcases/image/1312_Non-English Installation No Network
index 8269ac1..d44924d 100755
--- a/testcases/image/1312_Non-English Installation No Network
+++ b/testcases/image/1312_Non-English Installation No Network
@@ -1,57 +1,83 @@
-The goal of this test case is to check that localization support is functional during the installation, that language packs are installed correctly for those languages in the ISO, and that an informational message is shown prompting users to connect to the Internet to fully install language support for those languages not included.
-<dl>
-    <dt>Boot up the image.  When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
-        <dd>Language list will appear</dd>
-    <dt>Use arrow keys to select language and press Enter</dt>
-        <dd>Language list will close.</dd>
-        <dd>The installer is localized from this screen onwards</dd>
-    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
-        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
-    <dt>Click on the Install FAMILY icon</dt>
-        <dd>The 'Keyboard Layout' screen appears</dd>
-        <dd>The proposed keyboard corresponds with your keyboard</dd>
-	<dt>Select your keyboard layout and click on continue</dt>
-        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
-    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
-        <dd>Available options should represent the state of your system accurately</dd>
-        <dd>
-            <ul>
-                <li>(If network is available) Download updates while installing FAMILY</li>
-                <li>(If on a 'laptop') Is plugged to a power source</li>
-                <li>Install third-party software ... option available</li>
-            </ul>
-        </dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Installation type' screen is displayed</dd>
-    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
-        <dd>The 'Erase disk and install FAMILY' radio button is selected</dd>
-    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
-        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
-    <dt>If there is only one hard disk, skip to step 12 (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
-        <dd>Selected drive is displayed on the chart</dd>
-    <dt>Verify that the full drive space is allocated</dt>
-        <dd>Full drive space is allocated for installation</dd>
-    <dt>Click on Install Now button</dt>
-        <dd>The 'Where are you?' screen is displayed</dd>
-    <dt>Select your timezone, and click on the continue button</dt>
-        <dd>The 'Who are you?' screen appears</dd>
-    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
-        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
-        <dd>The installer's slideshow slides are completely localized</dd>
-    <dt>Wait for the installer to finish</dt>
-        <dd>An 'Installation Complete' dialog appears</dd>
-    <dt>Click the Restart now button</dt>
-        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
-    <dt>Remove the disc and press enter</dt>
-        <dd>The machine has been rebooted</dd>
-    <dt>Allow the machine to reboot</dt>
-        <dd>The system boots properly and loads into FAMILY</dd>
-    <dt>Verify that your system is localized</dt>
-        <dd>If you aren't using Bengali (bn), German (de), English (en), Spanish (es), French(fr), Portuguese (pt) or Xhosa (xh) a pop up appears showing saying your language support is not complete</dd>
-        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
-        <dd>If you are using one of the above languages the calendar shows the regional settings correctly</dd>
-</dl>
-<strong>If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
-    If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result.</strong>
+<!-- Please do not edit this file directly; it was generated with the
+     tools/test_case_gen script using the following configuration as input:
+     tools/non_english_installation.xml
+-->
+
+    <p>The goal of this test case is to check that localization support is
+      functional during the installation, 
+      and that language packs are installed correctly for those languages
+      in the ISO, and that an informational message is shown prompting
+      users to connect to the Internet to fully install language support
+      for those languages not included.
+    </p>
+    <dl>
+      
+    
+    <dt>Boot up the image. When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
+        <dd>Language list will appear</dd>
+    <dt>Use arrow keys to select language and press Enter</dt>
+        <dd>Language list will close.</dd>
+        <dd>The installer is localized from this screen onwards</dd>
+    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
+        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the Install FAMILY icon</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
+    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
+        <dd>The 'Erase disk and install FAMILY' radio button is selected</dd>
+    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
+        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
+    <dt>If there is only one hard disk, skip two steps to selecting the timezone (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
+        <dd>Selected drive is displayed on the chart</dd>
+    <dt>Verify that the full drive space is allocated</dt>
+        <dd>Full drive space is allocated for installation</dd>
+    <dt>Click on Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  
+    
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
+        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The installer's slideshow slides are completely localized</dd>
+  
+    
+    <dt>Verify that your system is localized</dt>
+        <dd>If you aren't using Bengali (bn), German (de), English (en), Spanish (es), French(fr), Portuguese (pt) or Xhosa (xh) a pop up appears showing saying your language support is not complete</dd>
+        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
+        <dd>If you are using one of the above languages the calendar shows the regional settings correctly</dd>
+  
+    
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine has been rebooted</dd>
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY</dd>
+  
+  
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  
\ No newline at end of file
diff --git a/testcases/image/1313_Installation Broken Internet b/testcases/image/1313_Installation Broken Internet
index 710d843..f654720 100755
--- a/testcases/image/1313_Installation Broken Internet
+++ b/testcases/image/1313_Installation Broken Internet
@@ -1,61 +1,87 @@
-The goal of this test case is to check that localization support is functional during the installation, that language packs are installed correctly for those languages in the ISO. During the lang-packs installation, Internet breaks, and the installer handles it gracefully. After the installation is complete and the new system rebooted an informational message is shown prompting users to connect to the Internet to fully install language support.
-
-<em>Proceed in your native language if you wish. Instructions will remain in English</em>
-
-<dl>
-    <dt>Boot up the image.  When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
-        <dd>Language list will appear</dd>
-    <dt>Use arrow keys to select language and press Enter</dt>
-        <dd>Language list will close.</dd>
-        <dd>The installer is localized from this screen onwards</dd>
-    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
-        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
-    <dt>Click on the Install FAMILY icon</dt>
-        <dd>The 'Keyboard Layout' screen appears</dd>
-        <dd>The proposed keyboard corresponds with your keyboard</dd>
-	<dt>Select your keyboard layout and click on continue</dt>
-        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
-    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
-        <dd>Available options should represent the state of your system accurately</dd>
-        <dd>
-            <ul>
-                <li>(If network is available) Download updates while installing FAMILY</li>
-                <li>(If on a 'laptop') Is plugged to a power source</li>
-                <li>Install third-party software ... option available</li>
-            </ul>
-        </dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Installation type' screen is displayed</dd>
-    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
-        <dd>The 'Erase disk and install FAMILY' radio button should be checked</dd>
-    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
-        <dd>If there is only one hard disk, the installer should skip to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
-    <dt>If there is only one hard disk, skip to step 10 (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
-        <dd>Selected drive is displayed on the chart</dd>
-    <dt>Verify that the full drive space is allocated</dt>
-        <dd>Full drive space is allocated for installation</dd>
-    <dt>Click on Install Now button</dt>
-        <dd>The 'Where are you?' screen is displayed</dd>
-    <dt>Select your timezone, and click on the continue button</dt>
-        <dd>The 'Who are you?' screen appears</dd>
-    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
-        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
-    <dt>Click on the continue button</dt>
-        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
-        <dd>The installer's slideshow slides are localized</dd>
-    <dt>When the installer is installing the language packs, disconnect the Internet</dt>
-    <dt>Wait for the installer to finish</dt>
-        <dd>An 'Installation Complete' dialog appears</dd>
-    <dt>Click the Restart now button</dt>
-        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
-    <dt>Remove the disc and press enter</dt>
-        <dd>The machine has been rebooted</dd>
-    <dt>Allow the machine to reboot</dt>
-        <dd>The system boots properly and loads into FAMILY</dd>
-    <dt>Verify that your system is localized</dt>
-        <dd>If you aren't using Bengali (bn), German (de), English (en), Spanish (es), French(fr), Portuguese (pt) or Xhosa (xh) a pop up appears showing saying your language support is not complete</dd>
-        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
-        <dd>If you are using one of the above languages the calendar shows the regional settings correctly</dd>
-</dl>
-<strong>If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
-    If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result.</strong>
+<!-- Please do not edit this file directly; it was generated with the
+     tools/test_case_gen script using the following configuration as input:
+     tools/non_english_installation.xml
+-->
+
+    <p>The goal of this test case is to check that localization support is
+      functional during the installation, 
+      and that language packs are installed correctly for those languages
+      in the ISO. During the lang-packs installation, Internet breaks, and the
+      installer handles it gracefully. After the installation is complete and
+      the new system rebooted an informational message is shown prompting users
+      to connect to the Internet to fully install language support.
+    </p>
+    <dl>
+      
+    
+    <dt>Boot up the image. When you see the aubergine screen with an icon at the bottom, press any key to get the menu</dt>
+        <dd>Language list will appear</dd>
+    <dt>Use arrow keys to select language and press Enter</dt>
+        <dd>Language list will close.</dd>
+        <dd>The installer is localized from this screen onwards</dd>
+    <dt>Boot up the iso using a CD/DVD or USB Key to a Live Session</dt>
+        <dd>The system boots properly and loads the installer displaying Welcome dialog with language selection and 'Try FAMILY' and 'Install FAMILY' buttons</dd>
+    <dt>Click on the Install FAMILY icon</dt>
+        <dd>The 'Keyboard Layout' screen appears</dd>
+        <dd>The proposed keyboard corresponds with your keyboard</dd>
+    <dt>Select your keyboard layout and click on continue</dt>
+        <dd>The 'Preparing to install FAMILY' screen is displayed</dd>
+    <dt>On the screen Preparing to install FAMILY, note the availability of the following components</dt>
+        <dd>Available options should represent the state of your system accurately</dd>
+        <dd>
+            <ul>
+                <li>(If network is available) Download updates while installing FAMILY</li>
+                <li>(If on a 'laptop') Is plugged to a power source</li>
+                <li>Install third-party software ... option available</li>
+            </ul>
+        </dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Installation type' screen is displayed</dd>
+    <dt>Note the state of the 'Erase disk and install FAMILY' radio button</dt>
+        <dd>The 'Erase disk and install FAMILY' radio button is selected</dd>
+    <dt>Click on the continue button (if there is only one hard disk in the system, the button should read 'Install now')</dt>
+        <dd>If there is only one hard disk, the installer skips to the "Where are you?' screen. Otherwise, the 'Installation type' screen is displayed</dd>
+    <dt>If there is only one hard disk, skip two steps to selecting the timezone (On the 'Where are you?' screen...). Otherwise, on the 'Installation type' screen verify that the drive selected on the Select drive list corresponds to the drive on the chart (e.g /dev/sda)</dt>
+        <dd>Selected drive is displayed on the chart</dd>
+    <dt>Verify that the full drive space is allocated</dt>
+        <dd>Full drive space is allocated for installation</dd>
+    <dt>Click on Install Now button</dt>
+        <dd>The 'Where are you?' screen is displayed</dd>
+  
+    
+    <dt>When the installer is installing the language packs, disconnect the Internet</dt>
+  
+    
+    <dt>Select your timezone, and click on the continue button</dt>
+        <dd>The 'Who are you?' screen appears</dd>
+    <dt>Input your initial user details and password (Note admin can not be used - it is a dedicated Linux User)</dt>
+        <dd>Name, username and password are accepted. Additionally, the continue button is enabled</dd>
+    <dt>Click on the continue button</dt>
+        <dd>The 'Welcome to FAMILY ' slide is displayed</dd>
+        <dd>The installer's slideshow slides are completely localized</dd>
+  
+    
+    <dt>Verify that your system is localized</dt>
+        <dd>If you aren't using Bengali (bn), German (de), English (en), Spanish (es), French(fr), Portuguese (pt) or Xhosa (xh) a pop up appears showing saying your language support is not complete</dd>
+        <dd>For any language, the system has to be fully localized. Note: the translation coverage of some languages might not be complete</dd>
+        <dd>If you are using one of the above languages the calendar shows the regional settings correctly</dd>
+  
+    
+    <dt>Wait for the installer to finish</dt>
+        <dd>An 'Installation Complete' dialog appears</dd>
+    <dt>Click the Restart now button</dt>
+        <dd>GUI is shut down, a prompt to remove media and press Enter appears</dd>
+    <dt>Remove the disc and press enter</dt>
+        <dd>The machine has been rebooted</dd>
+    <dt>Allow the machine to reboot</dt>
+        <dd>The system boots properly and loads into FAMILY</dd>
+  
+  
+    </dl>
+    <p>If <strong>all</strong> actions produce the expected results listed,
+      please <a href="results#add_result">submit</a> a 'passed' result.</p>
+    <p>If <strong>any</strong> action fails, or produces an unexpected result,
+      please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include
+      the bug number when you <a href="results#add_result">submit</a> your
+      result.</p>
+  
\ No newline at end of file
diff --git a/testcases/image/1711_RaspberryPi 4 2GB Post-install b/testcases/image/1711_RaspberryPi 4 2GB Post-install
index e7a2be7..3d1d98a 100644
--- a/testcases/image/1711_RaspberryPi 4 2GB Post-install
+++ b/testcases/image/1711_RaspberryPi 4 2GB Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 4 2GB.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 4 2GB.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1719_RaspberryPi 4 4GB Post-install b/testcases/image/1719_RaspberryPi 4 4GB Post-install
index 1dfd9e3..2687249 100644
--- a/testcases/image/1719_RaspberryPi 4 4GB Post-install
+++ b/testcases/image/1719_RaspberryPi 4 4GB Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 4 4GB.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 4 4GB.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1720_RaspberryPi 4 8GB Post-install b/testcases/image/1720_RaspberryPi 4 8GB Post-install
index be18789..4c2f4c7 100644
--- a/testcases/image/1720_RaspberryPi 4 8GB Post-install
+++ b/testcases/image/1720_RaspberryPi 4 8GB Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 4 8GB.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 4 8GB.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1721_RaspberryPi 3B+ Post-install b/testcases/image/1721_RaspberryPi 3B+ Post-install
index 0bba152..64f3563 100644
--- a/testcases/image/1721_RaspberryPi 3B+ Post-install
+++ b/testcases/image/1721_RaspberryPi 3B+ Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 3B+.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 3B+.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1722_RaspberryPi 3B Post-install b/testcases/image/1722_RaspberryPi 3B Post-install
index bafdc1f..f854d38 100644
--- a/testcases/image/1722_RaspberryPi 3B Post-install
+++ b/testcases/image/1722_RaspberryPi 3B Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 3B.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 3B.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1723_RaspberryPi 3A+ Post-install b/testcases/image/1723_RaspberryPi 3A+ Post-install
index c8a2d81..9cccff8 100644
--- a/testcases/image/1723_RaspberryPi 3A+ Post-install
+++ b/testcases/image/1723_RaspberryPi 3A+ Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 3A+.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 3A+.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1724_RaspberryPi 2 Post-install b/testcases/image/1724_RaspberryPi 2 Post-install
index 257739f..093024e 100644
--- a/testcases/image/1724_RaspberryPi 2 Post-install
+++ b/testcases/image/1724_RaspberryPi 2 Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 2.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 2.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1725_RaspberryPi CM3 Post-install b/testcases/image/1725_RaspberryPi CM3 Post-install
index 554a216..ceacc25 100644
--- a/testcases/image/1725_RaspberryPi CM3 Post-install
+++ b/testcases/image/1725_RaspberryPi CM3 Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi Compute Module 3.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi Compute Module 3.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1726_RaspberryPi CM3+ Post-install b/testcases/image/1726_RaspberryPi CM3+ Post-install
index b5faf97..923ee2c 100644
--- a/testcases/image/1726_RaspberryPi CM3+ Post-install
+++ b/testcases/image/1726_RaspberryPi CM3+ Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi Compute Module 3+.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi Compute Module 3+.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1727_RaspberryPi CM3+ Lite Post-install b/testcases/image/1727_RaspberryPi CM3+ Lite Post-install
index 1e2088b..cd562ed 100644
--- a/testcases/image/1727_RaspberryPi CM3+ Lite Post-install
+++ b/testcases/image/1727_RaspberryPi CM3+ Lite Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi Compute Module 3+ Lite.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi Compute Module 3+ Lite.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/testcases/image/1740_RaspberryPi 400 Post-install b/testcases/image/1740_RaspberryPi 400 Post-install
index 0f6441e..9859df7 100644
--- a/testcases/image/1740_RaspberryPi 400 Post-install
+++ b/testcases/image/1740_RaspberryPi 400 Post-install
@@ -3,8 +3,8 @@
      tools/pi_image_cases.xml
 -->
 
-    <p>This test case is to be carried out on a Raspberry Pi 400.</p>
     
+    <p>This test case is to be carried out on a Raspberry Pi 400.</p>
     <p>Follow the installation steps at <a href="https://ubuntu.com/download/iot/installation-media";>
       IoT installation media</a>
     </p>
diff --git a/tools/test_case_gen b/tools/test_case_gen
index 4309c11..39ead82 100755
--- a/tools/test_case_gen
+++ b/tools/test_case_gen
@@ -21,30 +21,25 @@ import argparse
 from copy import deepcopy
 from pathlib import Path
 from xml.etree import ElementTree as et
-
-
-class ut:
-    """
-    A trivial class to define the XML namespace used in the configuration file
-    and the (expanded) names of all the tags specific to that namespace.
-    """
-    ns       = 'com.ubuntu.tests'
-    config   = f'{{{ns}}}configuration'
-    template = f'{{{ns}}}template'
-    tests    = f'{{{ns}}}tests'
-    test     = f'{{{ns}}}test'
-    case     = f'{{{ns}}}case'
-    define   = f'{{{ns}}}define'
-    include  = f'{{{ns}}}include'
-    var      = f'{{{ns}}}var'
-    action   = f'{{{ns}}}action'
-    expected = f'{{{ns}}}expected'
+from collections.abc import Mapping
 
 
 def main(args=None):
     if sys.version_info < (3, 6):
         raise SystemExit('This script requires Python 3.6 or later')
 
+    if int(os.environ.get('TEST', '0')):
+        # To run the test suite (via the built-in doctest module):
+        #
+        #   $ TEST=1 ./tools/test_case_gen
+        #
+        # Optionally, if you have python3-coverage installed, and you want to
+        # track the coverage of the test suite you can further do:
+        #
+        #   $ TEST=1 python3-coverage run --source=tools/ ./tools/test_case_gen
+        #   $ python3-coverage report --show-missing
+        return _test_main()
+
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument(
         'config', type=argparse.FileType('r'), metavar='XML-FILE',
@@ -52,22 +47,12 @@ def main(args=None):
     parser.add_argument(
         '-o', '--output', default='testcases/image/', type=Path, metavar='DIR',
         help="The output directory; default: %(default)s")
-    parser.add_argument(
-        '--action-tag', default='dt', metavar='TAG',
-        help="The HTML tag to wrap <ut:action> content in; default: "
-        "%(default)s")
-    parser.add_argument(
-        '--expected-tag', default='dd', metavar='TAG',
-        help="The HTML tag to wrap <ut:expected> content in; default: "
-        "%(default)s")
 
     ns = parser.parse_args(args)
 
     try:
         for filename, doc in TestConfiguration(
-                ns.config,
-                action_tag=ns.action_tag,
-                expected_tag=ns.expected_tag):
+                et.fromstring(ns.config.read())).items():
             content = et.tostring(doc, encoding='unicode', method='html')
             # Hack off the outer elements as the result is effectively an
             # incomplete snippet of HTML. We can't do this with etree as
@@ -89,7 +74,7 @@ def main(args=None):
         else:
             typ, value, tb = sys.exc_info()
             print(value, file=sys.stderr)
-            sys.exit(1)
+            return 1
 
 
 def substitute(doc, path, content):
@@ -100,19 +85,51 @@ def substitute(doc, path, content):
     :class:`~xml.etree.ElementTree.Element` instance. The resulting document
     is returned (the original *doc* is not modified).
 
-    *content* can also be a callable. In this case, for each element matching
+    *content* may also be a callable. In this case, for each element matching
     *path*, the *content* callable will be called with *doc* and the matching
     element. It must return a single :class:`~xml.etree.ElementTree.Element`
     containing the content to substitute.
 
-    .. _XPath: https://docs.python.org/3/library/xml.etree.elementtree.html#elementtree-xpath
-
     .. note::
 
-        Note that the *contents* elements themselves are *not* included in the
+        Note that the *content* element itself is *not* included in the
         substitution. Only the descendents (including text, child elements,
-        etc.) of *contents* are substituted, and the substitution replaces the
-        placeholder element located by *path* entirely.
+        etc.) of *content* are substituted, and the substitution replaces the
+        placeholder elements located by *path* entirely.
+
+    For example::
+
+        >>> from xml.etree import ElementTree as et
+        >>> xml = et.fromstring('<p>This is a <color /> fruit</p>')
+        >>> color = et.fromstring('<color>red</color>')
+        >>> print(et.tostring(substitute(xml, './color', color)))
+        b'<p>This is a red fruit</p>'
+
+    There is no requirement that the placeholder element ("color" in the above
+    example) need match the substituted element::
+
+        >>> xml = et.fromstring('<p>This is a <subst /> fruit</p>')
+        >>> color = et.fromstring('<color>green</color>')
+        >>> print(et.tostring(substitute(xml, './subst', color)))
+        b'<p>This is a green fruit</p>'
+
+    You may also have placeholders at any depth in the document (provided the
+    specified *path* still matches), and the substitution element may contain
+    as many children as you wish::
+
+        >>> xml = et.fromstring('<a><b><c /></b></a>')
+        >>> subst = et.fromstring('<subst><c><d><e>f</e></d></c></subst>')
+        >>> print(et.tostring(substitute(xml, './/c', subst)))
+        b'<a><b><c><d><e>f</e></d></c></b></a>'
+
+    Finally, multiple placeholders may be present in the document::
+
+        >>> xml = et.fromstring('<a>First<b />second<b />third</a>')
+        >>> subst = et.fromstring('<b>, </b>')
+        >>> print(et.tostring(substitute(xml, './b', subst)))
+        b'<a>First, second, third</a>'
+
+    .. _XPath: https://docs.python.org/3/library/xml.etree.elementtree.html#elementtree-xpath
     """
     doc = deepcopy(doc)
     parent_map = {
@@ -128,7 +145,9 @@ def substitute(doc, path, content):
         content = content_fun(doc, placeholder)
         parent = parent_map[placeholder]
         parent_index = list(parent).index(placeholder)
-        if content.text is not None:
+        if content.text is None:
+            last = parent
+        else:
             if parent_index == 0:
                 if parent.text is None:
                     parent.text = content.text
@@ -146,38 +165,88 @@ def substitute(doc, path, content):
             last = deepcopy(elem)
             parent.insert(parent_index, last)
             parent_index += 1
-        if last is parent:
-            if last.text is None:
-                last.text = placeholder.tail
-            else:
-                last.text += placeholder.tail
-        else:
-            if last.tail is None:
-                last.tail = placeholder.tail
+        if placeholder.tail is not None:
+            if last is parent:
+                if last.text is None:
+                    last.text = placeholder.tail
+                else:
+                    last.text += placeholder.tail
             else:
-                last.tail += placeholder.tail
+                if last.tail is None:
+                    last.tail = placeholder.tail
+                else:
+                    last.tail += placeholder.tail
         parent.remove(placeholder)
     return doc
 
 
-class TestConfiguration:
+class ut:
+    """
+    A trivial class to define the XML namespace used in the configuration file
+    and the (expanded) names of all the tags specific to that namespace.
+    """
+    ns       = 'urn:com:ubuntu:tests'
+    config   = f'{{{ns}}}configuration'
+    template = f'{{{ns}}}template'
+    tests    = f'{{{ns}}}tests'
+    test     = f'{{{ns}}}test'
+    case     = f'{{{ns}}}case'
+    define   = f'{{{ns}}}define'
+    include  = f'{{{ns}}}include'
+    var      = f'{{{ns}}}var'
+
+
+class TestConfiguration(Mapping):
     """
-    Given *xml_file*, a file-like object containing a compatible XML
-    configuration for this script, this object will act as an iterator which
-    yields tuples of (filename, xml-document), where "xml-document" is the
-    :class:`~xml.etree.ElementTree.Element` at the root of the generated test
-    case document.
+    Given *root*, an :class:`~xml.etree.ElementTree.Element` containing a
+    compatible XML configuration for this script, this object will act as a
+    read-only :class:`~collections.abc.Mapping`
+    which maps filenames (taken from the "id" attribute of each <ut:case>
+    element in the configuration) to rendered test case documents, returned
+    as the root :class:`~xml.etree.ElementTree.Element` of the document.
+
+    For example::
+
+        >>> from xml.etree import ElementTree as et
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ...   <ut:template><dl><ut:tests /></dl></ut:template>
+        ...   <ut:test id="foo">
+        ...     <dt>What comes after <ut:var name="var" />?</dt>
+        ...     <dd>BAR</dd>
+        ...   </ut:test>
+        ...   <ut:case id="1_foo">
+        ...     <ut:define name="var">FOO</ut:define>
+        ...     <ut:include ref="foo" />
+        ...   </ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        >>> len(config)
+        1
+        >>> list(config)
+        ['1_foo']
+        >>> print(et.tostring(config['1_foo'], encoding='unicode'))
+        <ns0:template xmlns:ns0="urn:com:ubuntu:tests"><dl>
+        <BLANKLINE>
+            <dt>What comes after FOO?</dt>
+            <dd>BAR</dd>
+        <BLANKLINE>
+          </dl></ns0:template>
+        <BLANKLINE>
+
+    (the <BLANKLINE> markers above should be ignored; they're purely for the
+    benefit of doctest)
     """
-    def __init__(self, xml_file, *, action_tag='dt', expected_tag='dd'):
+    def __init__(self, root):
         super().__init__()
-        xml_config = et.fromstring(xml_file.read())
-        if xml_config.tag != ut.config:
+        if root.tag != ut.config:
             raise ValueError(
-                'Expected to find <ut:config> as the root element')
-        self._template = xml_config.find(f'./{ut.template}')
+                'Expected to find <ut:configuration> as the root element')
+        self._template = root.find(f'./{ut.template}')
         if self._template is None:
             raise ValueError(
-                'Expected to find <ut:template> under the <ut:config> '
+                'Expected to find <ut:template> under the <ut:configuration> '
                 'element')
         if self._template.find(f'.//{ut.tests}') is None:
             raise ValueError(
@@ -185,7 +254,7 @@ class TestConfiguration:
         try:
             self._tests = {
                 elem.attrib['id']: elem
-                for elem in xml_config.findall(f'./{ut.test}')
+                for elem in root.findall(f'./{ut.test}')
             }
         except KeyError:
             raise ValueError(f'Missing "id" attribute in {ut.test}')
@@ -193,11 +262,11 @@ class TestConfiguration:
             if not self._tests:
                 raise ValueError(
                     'Expected to find at least one <ut:test> under the '
-                    '<ut:config> element')
+                    '<ut:configuration> element')
         try:
             self._cases = {
                 elem.attrib['id']: elem
-                for elem in xml_config.findall(f'./{ut.case}')
+                for elem in root.findall(f'./{ut.case}')
             }
         except KeyError:
             raise ValueError('Missing "id" attribute in <ut:case>')
@@ -205,12 +274,17 @@ class TestConfiguration:
             if not self._cases:
                 raise ValueError(
                     'Expected to find at least one <ut:case> under the '
-                    '<ut:config> element')
-        self._action_tag = str(action_tag)
-        self._expected_tag = str(expected_tag)
-        self._env = self._build_env(root=xml_config)
+                    '<ut:configuration> element')
+        self._env = self._build_env(root)
 
-    def _build_env(self, *, root):
+    def _build_env(self, root):
+        """
+        Given a *root* element, finds all <ut:define> elements that are
+        direct children of the root, and returns a :class:`dict` mapping the
+        "name" attribute of each define to its corresponding element. A
+        :exc:`ValueError` is raised in the event that a "name" attribute is
+        not found on a define.
+        """
         try:
             return {
                 elem.attrib['name']: elem
@@ -219,12 +293,17 @@ class TestConfiguration:
         except KeyError:
             raise ValueError('Missing "name" attribute in <ut:define>')
 
+    def __len__(self):
+        return len(self._cases)
+
+    def __getitem__(self, key):
+        try:
+            return self._render_case(self._cases[key])
+        except ValueError as exc:
+            raise ValueError(f'In <ut:case id={key!r}>: {exc!s}')
+
     def __iter__(self):
-        for case_id, case in self._cases.items():
-            try:
-                yield case_id, self._render_case(case)
-            except ValueError as exc:
-                raise ValueError(f'In <ut:case id={case_id!r}>: {exc}')
+        return iter(self._cases)
 
     def _render_case(self, case):
         env = self._env.copy()
@@ -252,11 +331,8 @@ class TestConfiguration:
             raise ValueError(f'<ut:test id={test_ref!r}> not found')
         env = env.copy()
         env.update(self._build_env(root=include))
-        result = deepcopy(test)
-        result.find(f'./{ut.action}').tag = self._action_tag
-        result.find(f'./{ut.expected}').tag = self._expected_tag
         render = lambda doc, elem: self._render_var(doc, elem, env)
-        return substitute(result, f'.//{ut.var}', render)
+        return substitute(test, f'.//{ut.var}', render)
 
     def _render_var(self, include, var, env):
         try:
@@ -269,5 +345,341 @@ class TestConfiguration:
             raise ValueError(f'<ut:define name={var_name!r}> not found')
 
 
+def _test_main():
+    import doctest
+    os.environ.pop('TEST', None) # Don't run the test-suite recursively!
+    failures, total = doctest.testmod()
+    return bool(failures)
+
+# Some extra doctests to bump coverage
+
+__test__ = {
+    'substitute-prior-append': """
+    The positioning of the substitution placeholder shouldn't matter; it can
+    be the first child, or a later child::
+
+        >>> from xml.etree import ElementTree as et
+        >>> xml = et.fromstring('<p>This <em>is</em> a <color /> fruit</p>')
+        >>> color = et.fromstring('<color>blue</color>')
+        >>> print(et.tostring(substitute(xml, './color', color)))
+        b'<p>This <em>is</em> a blue fruit</p>'
+        >>> xml = et.fromstring('<p>This <em>was</em><color /> fruit</p>')
+        >>> color = et.fromstring('<color> yellow</color>')
+        >>> print(et.tostring(substitute(xml, './color', color)))
+        b'<p>This <em>was</em> yellow fruit</p>'
+    """,
+
+    'substitute-empty-cases': """
+    The substitution must succeed even if the text/tail properties of the
+    parent/prior elements are :data:`None`::
+
+        >>> from xml.etree import ElementTree as et
+        >>> xml = et.fromstring('<p><content />?</p>')
+        >>> content = et.fromstring('<content></content>')
+        >>> print(et.tostring(substitute(xml, './content', content)))
+        b'<p>?</p>'
+        >>> xml = et.fromstring('<p><content />?</p>')
+        >>> content = et.fromstring('<content>foo bar baz<br /></content>')
+        >>> print(et.tostring(substitute(xml, './content', content)))
+        b'<p>foo bar baz<br />?</p>'
+    """,
+
+    'config-bad-root': """
+    The document passed to TestConfiguration must have <ut:configuration> as
+    its root element::
+
+        >>> from xml.etree import ElementTree as et
+        >>> doc = et.fromstring("<a />")
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 258, in __init__
+            raise ValueError(
+        ValueError: Expected to find <ut:configuration> as the root element
+
+    And must have <ut:template> under <ut:configuration>::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <foo />
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 262, in __init__
+            raise ValueError(
+        ValueError: Expected to find <ut:template> under the <ut:configuration> element
+
+    Which must have <ut:tests> somewhere (indirectly) underneath it::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template>Some content, but no tests!</ut:template>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 266, in __init__
+            raise ValueError(
+        ValueError: <ut:template> must include <ut:tests> somewhere within it
+
+    There must also be <ut:test> elements::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 277, in __init__
+            raise ValueError(
+        ValueError: Expected to find at least one <ut:test> under the <ut:configuration> element
+
+    Which must have "id" attributes::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test></ut:test>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 274, in __init__
+            raise ValueError(
+        ValueError: Missing "id" attribute in {urn:com:ubuntu:tests}test
+
+    Finally, there must also be <ut:case> elements::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo"></ut:test>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 289, in __init__
+            raise ValueError(
+        ValueError: Expected to find at least one <ut:case> under the <ut:configuration> element
+
+    Which must also have "id" attributes::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo"></ut:test>
+        ... <ut:case></ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 286, in __init__
+            raise ValueError(
+        ValueError: Missing "id" attribute in <ut:case>
+    """,
+
+    'config-bad-defines': """
+    <ut:define> elements must all have a "name" attribute::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo"></ut:test>
+        ... <ut:case id="1_foo"></ut:case>
+        ... <ut:define>Some content</ut:define>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 310, in __init__
+            raise ValueError(
+        ValueError: Missing "name" attribute in <ut:define>
+    """,
+
+    'config-bad-include': """
+    <ut:include> elements must all have a "ref" attribute::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo"></ut:test>
+        ... <ut:case id="1_foo"><ut:include /></ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        >>> config['1_foo']
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 343, in __init__
+            raise ValueError(
+        ValueError: In <ut:case id='1_foo'>: Missing "ref" attribute in <ut:include>
+
+    And whatever <ut:test> is referenced must actually exist::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo"></ut:test>
+        ... <ut:case id="1_foo"><ut:include ref="bar" /></ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        >>> config['1_foo']
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 347, in __init__
+            raise ValueError(
+        ValueError: In <ut:case id='1_foo'>: <ut:test id='bar'> not found
+    """,
+
+    'config-bad-var': """
+    <ut:var> elements must have a "name" attribute::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo">
+        ...   <dt><ut:var /></dt>
+        ...   <dd></dd>
+        ... </ut:test>
+        ... <ut:case id="1_foo"><ut:include ref="foo" /></ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        >>> config['1_foo']
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 360, in __init__
+            raise ValueError(
+        ValueError: In <ut:case id='1_foo'>: Missing "name" attribute in <ut:var>
+
+    And whatever <ut:var> is referenced must actually exist::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:tests /></ut:template>
+        ... <ut:test id="foo">
+        ...   <dt><ut:var name="my-var" /></dt>
+        ...   <dd></dd>
+        ... </ut:test>
+        ... <ut:case id="1_foo"><ut:include ref="foo" /></ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        >>> config['1_foo']
+        Traceback (most recent call last):
+          File "<stdin>", line 3, in <module>
+            config = TestConfiguration(doc)
+          File "./tools/test_case_gen", line 364, in __init__
+            raise ValueError(
+        ValueError: In <ut:case id='1_foo'>: <ut:define name='my-var'> not found
+    """,
+
+    'config-template-var': """
+     Templates can contain <ut:var> elements just like <ut:test> can::
+
+        >>> doc = et.fromstring('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ... <ut:template><ut:var name="foo" /><ut:tests /></ut:template>
+        ... <ut:test id="foo"><dt /><dd /></ut:test>
+        ... <ut:case id="1_foo">
+        ...   <ut:define name="foo">FOO!</ut:define>
+        ...   <ut:include ref="foo" />
+        ... </ut:case>
+        ... </ut:configuration>
+        ... ''')
+        >>> config = TestConfiguration(doc)
+        >>> print(et.tostring(config['1_foo'], encoding='unicode'))
+        <ns0:template xmlns:ns0="urn:com:ubuntu:tests">FOO!
+          <dt /><dd />
+        </ns0:template>
+        <BLANKLINE>
+    """,
+
+    'main --help': """
+    Running the main function with --help results in help output::
+
+        >>> try:
+        ...     main(['--help']) # doctest: +ELLIPSIS
+        ... except SystemExit:
+        ...     pass
+        usage: test_case_gen [-h] [-o DIR] XML-FILE
+        ...
+    """,
+
+    'main works': """
+    Running the main function with a valid configuration results in output
+    to the specified directory::
+
+        >>> import io
+        >>> import tempfile
+        >>> from pathlib import Path
+        >>> tmp = tempfile.TemporaryDirectory()
+        >>> xml = io.open(Path(tmp.name) / 'config.xml', 'w')
+        >>> xml.write('''
+        ... <ut:configuration xmlns:ut="urn:com:ubuntu:tests">
+        ...   <ut:template>
+        ...     Before tests
+        ...     <ut:tests />
+        ...     After tests
+        ...   </ut:template>
+        ...   <ut:test id="button-test">
+        ...     <dt>Press the big <ut:var name="color" /> button</dt>
+        ...     <dd>Everything goes <ut:var name="result" /></dd>
+        ...   </ut:test>
+        ...   <ut:case id="1_dr_strangelove">
+        ...     <ut:define name="color">red</ut:define>
+        ...     <ut:define name="result">boom</ut:define>
+        ...     <ut:include ref="button-test" />
+        ...   </ut:case>
+        ... </ut:configuration>
+        ... ''')
+        483
+        >>> config_file = xml.name
+        >>> xml.close()
+        >>> main(['-o', tmp.name, config_file])
+        >>> output = Path(tmp.name) / '1_dr_strangelove'
+        >>> output.exists()
+        True
+        >>> print(output.read_text()) # doctest: +ELLIPSIS
+        <!-- Please do not edit this file directly; it was generated with the
+             tools/test_case_gen script using the following configuration as input:
+             .../config.xml
+        -->
+        <BLANKLINE>
+            Before tests
+        <BLANKLINE>
+        <BLANKLINE>
+            <dt>Press the big red button</dt>
+            <dd>Everything goes boom</dd>
+        <BLANKLINE>
+        <BLANKLINE>
+            After tests
+        <BLANKLINE>
+        >>> tmp.cleanup()
+    """,
+}
+
+
 if __name__ == '__main__':
-    main()
+    sys.exit(main())

Follow ups