Index: gs3-extensions/testing/trunk/src/build.xml
===================================================================
--- gs3-extensions/testing/trunk/src/build.xml	(revision 32701)
+++ gs3-extensions/testing/trunk/src/build.xml	(revision 32705)
@@ -6,12 +6,18 @@
 
   <property name="gli.home" value="${env.GSDL3SRCHOME}/gli"/>
+
+  <!-- define ext.test.folder so we can move some targets into toplevel GS3 ant build file -->
+  <property name="ext.test.folder" value="${basedir}" />
+  
+  <!-- where the reports will go -->
+  <property name="test.reports" value="${ext.test.folder}/reports" />
   
   <target name="build-util-jar" depends="needs-gs3-setup, needs-make-gli-jar">
-    <mkdir dir="${basedir}/build"/>
-    <javac srcdir="${basedir}/src"
-	   destdir="${basedir}/build"
+    <mkdir dir="${ext.test.folder}/build"/>
+    <javac srcdir="${ext.test.folder}/src"
+	   destdir="${ext.test.folder}/build"
 	   includeantruntime="${compile.includeantruntime}">
       <classpath>
-	<fileset dir="${basedir}/lib/java">
+	<fileset dir="${ext.test.folder}/lib/java">
 	  <include name="*.jar"/>
 	</fileset>
@@ -22,6 +28,6 @@
       <include name="org/greenstone/gsdl3/testing/*.java"/>			
     </javac>
-    <jar destfile="${basedir}/lib/java/GSTestingUtil.jar">
-      <fileset dir="${basedir}/build">
+    <jar destfile="${ext.test.folder}/lib/java/GSTestingUtil.jar">
+      <fileset dir="${ext.test.folder}/build">
 	<include name="org/greenstone/gsdl3/testing/**"/>			  
       </fileset>
@@ -58,11 +64,11 @@
 <target name="compile-tutorials-tests" depends="needs-gs3-setup, needs-make-gli-jar">
 
-  <mkdir dir="${basedir}/build"/>
+  <mkdir dir="${ext.test.folder}/build"/>
   <javac
-      srcdir="${basedir}/src"
-      destdir="${basedir}/build"
+      srcdir="${ext.test.folder}/src"
+      destdir="${ext.test.folder}/build"
       includeantruntime="${compile.includeantruntime}">
     <classpath>
-      <fileset dir="${basedir}/lib/java">
+      <fileset dir="${ext.test.folder}/lib/java">
 	<include name="*.jar"/>
       </fileset>
@@ -73,6 +79,6 @@
     <include name="gstests/tutorials/*.java"/>
   </javac>
-  <jar destfile="${basedir}/tutorial-tests.jar">
-    <fileset dir="${basedir}/build">
+  <jar destfile="${ext.test.folder}/tutorial-tests.jar">
+    <fileset dir="${ext.test.folder}/build">
       <include name="gstests/tutorials/**"/>
     </fileset>
@@ -84,12 +90,15 @@
 
 <!-- https://stackoverflow.com/questions/10704324/how-to-add-to-classpath-all-classes-from-set-of-directories-in-ant -->
-<path id="tutorials.path">  
-  <fileset dir="${basedir}/lib/java">
+<path id="tutorials.path">
+
+  <!-- Includes ant-nodeps-1.8.1 from 2010 that replaces 2005's optional.jar of ant
+       for generating reports -->
+  <fileset dir="${ext.test.folder}/lib/java">
     <include name="*.jar"/>
   </fileset>
-  <!--<fileset dir="${basedir}/build">	    
+  <!--<fileset dir="${ext.test.folder}/build">	    
     <include name="gstests/tutorials/**/*.class"/>
   </fileset>-->
-  <files includes="${basedir}/tutorial-tests.jar"/>
+  <files includes="${ext.test.folder}/tutorial-tests.jar"/>
 
   <!-- To run GLI, need GLI.jar and its help jars and folder: classes folder, apache.jar, jna.jar, jna-platform.jar, qfslib.jar, rsyntaxtextarea.jar -->
@@ -114,5 +123,5 @@
   <java classname="org.junit.runner.JUnitCore" dir="${gli.home}" fork="true" maxmemory="256m" classpathref="tutorials.path">
     <!--https://stackoverflow.com/questions/38676719/selenium-using-java-the-path-to-the-driver-executable-must-be-set-by-the-webdr-->
-    <sysproperty key="webdriver.gecko.driver" path="${basedir}/geckodriver"/>
+    <sysproperty key="webdriver.gecko.driver" path="${ext.test.folder}/geckodriver"/>
     <arg value="gstests.tutorials.RunGLITest"/>
     <!-- TODO: for now hardcoding the GS3 URL. Toplevel build.xml can properly construct this
@@ -120,7 +129,20 @@
     <jvmarg value="-DSERVERURL=http://127.0.0.1:8383/greenstone3/library "/>
   </java>
-  
-  
+
+  <!-- create reports dir if it doesn't exist -->
+  <mkdir dir="${test.reports}"/>
+  <!--
+      https://junit.org/junit4/faq.html#running_6
+      How do I use Ant to create HTML test reports?
+  -->
+  <junitreport todir="${test.reports}">
+    <fileset dir="${test.reports}">
+      <include name="TEST-*.xml" />
+    </fileset>
+    <report todir="${test.reports}" />
+  </junitreport>
 </target>
+
+<!-- TODO: check if after testing GLI we may need to ensure tomcat stopped? -->
 
 </project>
Index: gs3-extensions/testing/trunk/src/src/gstests/tutorials/RunGLITest.java
===================================================================
--- gs3-extensions/testing/trunk/src/src/gstests/tutorials/RunGLITest.java	(revision 32701)
+++ gs3-extensions/testing/trunk/src/src/gstests/tutorials/RunGLITest.java	(revision 32705)
@@ -13,4 +13,5 @@
 - https://web.archive.org/web/20120526191520/http://alexruiz.developerblogs.com/?p=160
 - https://web.archive.org/web/20130218063544/http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html
+- https://stackoverflow.com/questions/2829364/java-difference-between-swingworker-and-swingutilities-invokelater
 
 Got AssertJ Swing from Maven Central Repository:
@@ -117,10 +118,6 @@
 
       
-      // waiting 2 seconds for window, so we can see it
-      try{
-	Thread.sleep(5000);
-      } catch(Exception e) {
-	  e.printStackTrace();
-      }
+      // waiting a few seconds for window, so we can see it
+      PAUSE(2);
       
     
@@ -131,30 +128,46 @@
 
       window = findFrame("GUIManager").using(robot());
-      	
-	String gatherPaneLabel = Dictionary.get("GUI.Gather");
-	System.err.println("@@@ Expecting label: " + gatherPaneLabel);
+      
+      String gatherPaneLabel = Dictionary.get("GUI.Gather");
+      System.err.println("@@@ Expecting label: " + gatherPaneLabel);
+      
+      System.err.println("@@@ Second test: that Gather panel is selected and has right title");
+      
+      JTabbedPaneFixture tab = window.tabbedPane("GUIManager.tab_pane");
+      
+      
+      tab.requireSelectedTab(Index.atIndex(1));
+      tab.requireTitle(gatherPaneLabel, Index.atIndex(1));
+      
+      // attempt to switch to enrich pane, uses static methods of GSGUITestingUtil
+      // through static import of that class
+      switchToPane(window, DOWNLOAD_PANE);
 
-	System.err.println("@@@ Second test: that Gather panel is selected and has right title");
+      // For testing, want GLI to be in librarian mode by default
+      changeUserMode(window, "librarian");
 
-	JTabbedPaneFixture tab = window.tabbedPane("GUIManager.tab_pane");
+      switchToPane(window, GATHER_PANE);
+      
+      loadCollection(window, "lucene-jdbm-demo");
 
-	
-	tab.requireSelectedTab(Index.atIndex(1));
-	tab.requireTitle(gatherPaneLabel, Index.atIndex(1));
+      // wait a couple of seconds again?
+      PAUSE(2);      
+      closeCollection(window);
+      
+      createCollection(window, "pinky", "Pinky was here", null);
+      closeCollection(window); // collection must be closed in order to be deleted
+      deleteCollection(window, "pinky");
+      // wait a few of seconds again?
+      PAUSE(2);
+      
+      exportCollection(window, "GreenstoneMETS", "lucene-jdbm-demo");
 
-	// attempt to switch to enrich pane, uses static methods of GSGUITestingUtil
-	// through static import of that class
-	switchToPane(window, DOWNLOAD_PANE);
-
-	loadCollection(window, "lucene-jdbm-demo");
-
-	// wait a couple of seconds again?
-	 try{
-	     Thread.sleep(5000);
-	 } catch(Exception e) {
-	     e.printStackTrace();
-	 }
+      // Reset GLI to expert mode for regular GLI use
+      changeUserMode(window, "expert");
       
-	
+      PAUSE(5);
+      //exitGLI(window);
+      
+      switchToPane(window, GATHER_PANE);
   }
 
@@ -165,4 +178,5 @@
     {
 	_driver.quit();
+	
     }
 }
Index: gs3-extensions/testing/trunk/src/src/org/greenstone/gsdl3/testing/GSGUITestingUtil.java
===================================================================
--- gs3-extensions/testing/trunk/src/src/org/greenstone/gsdl3/testing/GSGUITestingUtil.java	(revision 32701)
+++ gs3-extensions/testing/trunk/src/src/org/greenstone/gsdl3/testing/GSGUITestingUtil.java	(revision 32705)
@@ -1,5 +1,7 @@
 package org.greenstone.gsdl3.testing;
 
+import java.io.File;
 import java.util.Map;
+import java.util.regex.*;
 import java.awt.Component;
 
@@ -7,4 +9,6 @@
 import org.junit.Assert;
 import org.assertj.swing.fixture.*;
+import org.assertj.swing.timing.Timeout ;
+	    
 
 // GLI imports
@@ -18,5 +22,9 @@
 
 public class GSGUITestingUtil
-{ 
+{
+    public static final boolean PAUSE_ON = true;
+    
+    public static final int SECOND = 1000; // 1000 ms
+    
     public static final String DOWNLOAD_PANE = "Download"; 
     public static final String GATHER_PANE = "Gather"; 
@@ -33,4 +41,19 @@
 
     /************************ GENERAL *******************************/
+    public static void PAUSE() {	
+	PAUSE(5);
+    }
+    public static void PAUSE(int seconds) {
+	if(!PAUSE_ON) return; // ignore calls to PAUSE
+	
+	// wait a couple of seconds again?
+	 try{
+	     Thread.sleep(seconds*SECOND);
+	 } catch(Exception e) {
+	     e.printStackTrace();
+	 }
+    }
+    
+    
     //https://joel-costigliola.github.io/assertj/swing/api/org/assertj/swing/fixture/FrameFixture.html
     public static void switchToPane(FrameFixture window, String pane) {
@@ -49,4 +72,13 @@
     }
 
+    /*
+    public static void stealAnyLock(FrameFixture window) {
+	DialogFixture dialog = window.dialog("LockFileDialog");
+	if(dialog != null) {
+	    dialog.button("LockFileDialog.ok_button").click();
+	}
+    }*/
+
+    
     // e.g. pane = Enrich,view=collection; pane = Gather, view = workspace (or collection)
     public static void getFolderPath(String pane, String view, String folderPath) {}
@@ -54,19 +86,172 @@
     
     /**********************FILE MENU*******************************/
-    public static void setPrefs(String tab, Map params) {}
-    // mode e.g. librarian, expert
-    public static void changeUserMode(String mode) {}
-    
-    public static void deleteCollection(){}
-    public static void saveCollection(){}
-    public static void closeCollection(){}    
-    public static void createCollection(String basedOn) {}    
-    public static void loadCollection(FrameFixture window, String colName) {
+    public static void setPrefs(FrameFixture window, String tab, Map params) {
+	openMenuItem(window, "file", "options");
+
+    }
+    
+    /**
+     * @param toMode must be the dictionary key for Preferences mode.
+     * Choose from Preferences.Mode.Assistant, Preferences.Mode.Librarian, Preferences.Mode.Expert
+     */
+    public static void changeUserMode(FrameFixture window, String toMode) {
+	openMenuItem(window, "file", "options"); // click MenuBar.file -> MenuBar.file_options
+
+	// switch to Mode tab
+	String paneLabel = Dictionary.get("Preferences.Mode");
+	DialogFixture dialog = window.dialog("Preferences");
+	JTabbedPaneFixture tab = window.tabbedPane("Preferences.tab_pane");
+	tab.selectTab(paneLabel); // select tab by its title
+
+	String mode = "assistant"; // library assistant
+	if(toMode.contains("xpert")) {
+	    mode = "expert";
+	} else if (toMode.endsWith("ibrarian")) {
+	    mode = "librarian";
+	}
+	dialog.radioButton("Preferences."+mode+"_mode_radio_button").check(true);
+
+	// apply and close dialog
+	dialog.button("Preferences.apply_button").click();
+	dialog.button("Preferences.ok_button").click();
+    }
+
+    public static void exitGLI(FrameFixture window) {
+	openMenuItem(window, "file", "exit");
+	PAUSE(5);
+    }
+    
+    public static void closeCollection(FrameFixture window){
+	// Through componennt names, clicks on MenuBar.file then MenuBar.file_close
+	openMenuItem(window, "file", "close");
+    }
+
+    public static void deleteCollection(FrameFixture window, String collName) {
+	openMenuItem(window, "file", "delete");
+	DialogFixture dialog = window.dialog("DeleteCollectionPrompt"); // could call window.dialg() too as there will be only one, unless GLI run in debug mode?
+	
+	JListFixture collection_list = dialog.list("DeleteCollectionPrompt.list");
+	
+	Pattern collNameRegex = Pattern.compile(".*"+collName+".*");
+	collection_list.selectItem(collNameRegex);
+	
+	collection_list.requireSelection(collNameRegex); // assert it exists and is selectable. Can't select (or delete) coll if it's open
+	
+	dialog.checkBox("DeleteCollectionPrompt.confirmation").check(true); // check checkbox to be sure to delete
+	dialog.button("DeleteCollectionPrompt.ok_button").click(); // delete button
+
+	// A close confirmation optionPane dialog appears - click OK in it
+	dialog.optionPane().okButton().click();
+	
+	dialog.button("DeleteCollectionPrompt.close_button").click(); // close deleteColl dialog
+    }
+    
+    public static void saveCollection(FrameFixture window) {
+	openMenuItem(window, "file", "save"); // MenuBar.file -> MenuBar.file_save
+    }
+
+    public static void createCollection(FrameFixture window,
+					String collTitle, String collDescription, String baseColl)
+    {
+	openMenuItem(window, "file", "new");
+	DialogFixture dialog = window.dialog("col"); // GLI is unable to setName() of this dialog
+	// to NewCollectioNDetailsPrompt. Not sure why.
+
+	dialog.textBox("NewCollectionDetailsPrompt.title").enterText(collTitle); // JTextField
+	if(collDescription != null && !collDescription.equals("")) {
+	    // JTextArea description
+	    dialog.textBox("NewCollectionDetailsPrompt.description").enterText(collDescription);
+	}
+
+	// JComboBox base_collection
+	if(baseColl != null && !baseColl.equals("")) {
+	    JComboBoxFixture baseCollBox = dialog.comboBox("NewCollectionDetailsPrompt.base_collection");
+	    Pattern collNameRegex = Pattern.compile(".*"+baseColl+".*"); // comboBox should CONTAIN this string
+	    baseCollBox.selectItem(collNameRegex);
+	    baseCollBox.requireSelection(collNameRegex); // assert the baseColl name exists	    
+	}
+	dialog.button("NewCollectionDetailsPrompt.create_button").click();
+    }
+
+    public static void loadCollection(FrameFixture window, String collName) {
 	openMenuItem(window, "file", "open");
-	
-    }
-
-    // only for GS2?
-    public static void exportCollection() {}
+	DialogFixture dialog = window.dialog("OpenCollectionDialog");
+	JListFixture collection_list = dialog.list("OpenCollectionDialog.collection_list");
+
+	// See section 4.2 of http://www.vogella.com/tutorials/JavaRegularExpressions/article.html
+	// Apparently, testing Pattern "collName" expects it to match in entirety
+	// whereas testing ".*collName.*" will test the string contains this Pattern
+	Pattern collNameRegex = Pattern.compile(".*"+collName+".*");
+	
+	/*String[] items = collection_list.contents();
+	for(String item : items) {
+	    System.err.println("@@@ ITEM: " + item);
+	    }*/
+	
+
+	//JListItemFixture collItem = collection_list.item(collNameRegex);
+	//collItem.click();
+	//collection_list.requireSelection(collNameRegex); // assert that the requested collection exists
+	//collItem.doubleClick();
+	collection_list.selectItem(collNameRegex);
+	collection_list.requireSelection(collNameRegex); // assert that the requested collection exists
+
+	
+	/*String[] selections = collection_list.selection();
+	for(String s : selections) {
+	    System.err.println("@@@ opening collection " + s);
+	    }*/
+
+	JListItemFixture selectedColl = collection_list.item(collNameRegex);
+	//collection_list.doubleClick(); // will double-click on last item regardless of what's selected
+	selectedColl.doubleClick();
+
+	//steal any lock
+	/*
+	DialogFixture dialog = window.dialog("LockFileDialog");
+	if(dialog != null) {
+	    dialog.button("LockFileDialog.ok_button").click();
+	}
+	*/
+    }
+
+    /**
+     * @param exportColl has to be internal GS collection name, e.g. lucene-jdbm-demo
+     * not public collection name like "Demo Collection".
+     */
+    public static void exportCollection(FrameFixture window, String exportToFormat, String exportColl)
+    {
+	openMenuItem(window, "file", "exportas"); // Clicks MenuBar.file -> MenuBar.file_exportas
+
+	DialogFixture dialog = window.dialog("ExportAsPrompt");	
+	dialog.comboBox("ExportAsPrompt.saveas_combobox").selectItem(exportToFormat);
+	
+	Pattern collNameRegex = Pattern.compile(".*"+exportColl+".*");
+	JListFixture collection_list = dialog.list("ExportAsPrompt.list");
+	collection_list.selectItem(collNameRegex); //JList of collections
+	collection_list.requireSelection(collNameRegex); // check it exists
+
+	dialog.button("ExportAsPrompt.ok_button").click(); // do export
+
+	// first we see a progress dialog
+	
+	// Wait for subsequent export complete dialog
+	DialogFixture exportCompleteDialog = window.dialog("SimpleResultDialog", Timeout.timeout(30*SECOND));
+
+	// non-modal dialog, so give it focus, so we can close it through its Close button
+	exportCompleteDialog.focus();
+	
+	//exportCompleteDialog.button().click();
+	exportCompleteDialog.button("SimpleResultDialog.GLIButton."+Dictionary.get("General.Close")).click(); // it only has a single button: "Close", still want to future proof if more buttons get added by specifying the particular button
+
+
+	// Close outer dialog too - cancel doubles as close button now that export is complete
+	dialog.button("ExportAsPrompt.cancel_button").click();
+	
+	File file = new File(System.getenv("GSDLHOME") + File.separator + "tmp" + File.separator + "exported_"+exportColl+"_"+exportToFormat);
+
+	Assert.assertTrue(file.exists());
+	Assert.assertTrue(file.isDirectory());	
+    }
     
     /*************** DESIGN ******************************/
