From eea174825930f5de82843297e11221c78a423d60 Mon Sep 17 00:00:00 2001
From: Simon Crowle <sgc@it-innovation.soton.ac.uk>
Date: Wed, 20 Dec 2017 16:04:02 +0000
Subject: [PATCH] Adds foundation test framework for CLMC montioring
 specification

---
 README.md                                     |  32 +-
 src/clmc-spec/.classpath                      |  31 ++
 src/clmc-spec/.project                        |  23 ++
 .../org.eclipse.core.resources.prefs          |   5 +
 .../.settings/org.eclipse.jdt.core.prefs      |   5 +
 .../.settings/org.eclipse.m2e.core.prefs      |   4 +
 src/clmc-spec/.vscode/settings.json           |   3 +
 src/clmc-spec/pom.xml                         |  51 +++
 .../main/resources/inputs/host_resource_input |   9 +
 .../resources/monSpecTestConfig.properties    |   8 +
 .../resources/queries/host_resource_query     |   4 +
 .../clmc/monspec/test/AlphaTestSuite.java     |  72 ++++
 .../clmc/monspec/test/BasicInputTest.java     |  77 +++++
 .../clmc/monspec/test/BasicQueryTest.java     |  75 +++++
 .../clmc/monspec/test/base/BaseTest.java      | 137 ++++++++
 .../clmc/monspec/test/base/InfluxUtility.java | 309 ++++++++++++++++++
 .../clmc/monspec/test/base/TestSuiteBase.java |  94 ++++++
 17 files changed, 938 insertions(+), 1 deletion(-)
 create mode 100644 src/clmc-spec/.classpath
 create mode 100644 src/clmc-spec/.project
 create mode 100644 src/clmc-spec/.settings/org.eclipse.core.resources.prefs
 create mode 100644 src/clmc-spec/.settings/org.eclipse.jdt.core.prefs
 create mode 100644 src/clmc-spec/.settings/org.eclipse.m2e.core.prefs
 create mode 100644 src/clmc-spec/.vscode/settings.json
 create mode 100644 src/clmc-spec/pom.xml
 create mode 100644 src/clmc-spec/src/main/resources/inputs/host_resource_input
 create mode 100644 src/clmc-spec/src/main/resources/monSpecTestConfig.properties
 create mode 100644 src/clmc-spec/src/main/resources/queries/host_resource_query
 create mode 100644 src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java
 create mode 100644 src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java
 create mode 100644 src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java
 create mode 100644 src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java
 create mode 100644 src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java
 create mode 100644 src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java

diff --git a/README.md b/README.md
index 999b28c..4baf807 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,36 @@
 |Authors|Organisation|                    
 |-|-|
 |[Michael Boniface](mailto:mjb@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)|
+|[Simon Crowle](mailto:sgc@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)|
 
+### Contents
 
-#### Contents
\ No newline at end of file
+#### CLMC Monitoring Specification Test Framework
+A Java/JUnit test framework has been developed to provide concrete examples of the CLMC monitoring specification. To build and run this test framework you will need:
+
+1. The CLMC TICK stack installed and running (provided as a Vagrant solution in this project)
+
+2. Java JDK 1.8+ installed
+3. Maven 3+ installed
+  - Optionally a Java IDE installed, such as NetBeans
+
+
+##### Building the test framework
+
+1. Clone this project (obviously)
+
+2. Open the Maven project (\<flame-clmc root\>\src\clmc-spec) in your Java IDE or navigate to POM.xml file in command line
+
+3. Check the monSpecTestConfig.properties file (src/main/resources) matches your TICK stack set-up. It's likely to.  
+
+4.  Build the project (this should automatically build and run the tests)
+    > From the command line: mvn test
+
+
+##### Extending the test framework
+This test framework is easily extendible. There are two simple tests already ready for you to explore:
+
+* BasicInputTest.java - This tries sending some basic metrics about a host to InfluxDB
+* BasicQueryTest.java - This tries querying InfluxDB about host metrics
+
+Each test case uses resources in the project to send test data or execute queries. In the first case the resource '/src/main/resources/inputs/host_resource_input' is a file with example InfluxDB Line Protocol statements. In the second test case, the file '/src/main/resources/host_resource_query' contains queries that are executed against InfluxDB. The results of these are currently just output to the console.
\ No newline at end of file
diff --git a/src/clmc-spec/.classpath b/src/clmc-spec/.classpath
new file mode 100644
index 0000000..6d7587a
--- /dev/null
+++ b/src/clmc-spec/.classpath
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/src/clmc-spec/.project b/src/clmc-spec/.project
new file mode 100644
index 0000000..061590a
--- /dev/null
+++ b/src/clmc-spec/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>clmc-spec</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/src/clmc-spec/.settings/org.eclipse.core.resources.prefs b/src/clmc-spec/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..839d647
--- /dev/null
+++ b/src/clmc-spec/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/src/clmc-spec/.settings/org.eclipse.jdt.core.prefs b/src/clmc-spec/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..714351a
--- /dev/null
+++ b/src/clmc-spec/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/src/clmc-spec/.settings/org.eclipse.m2e.core.prefs b/src/clmc-spec/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/src/clmc-spec/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/src/clmc-spec/.vscode/settings.json b/src/clmc-spec/.vscode/settings.json
new file mode 100644
index 0000000..e0f15db
--- /dev/null
+++ b/src/clmc-spec/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "java.configuration.updateBuildConfiguration": "automatic"
+}
\ No newline at end of file
diff --git a/src/clmc-spec/pom.xml b/src/clmc-spec/pom.xml
new file mode 100644
index 0000000..fa2828e
--- /dev/null
+++ b/src/clmc-spec/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>uk.ac.soton.itinnovation.flame</groupId>
+    <artifactId>clmc-spec</artifactId>
+    <version>0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+    
+    <dependencies>
+        
+        <dependency>
+            <groupId>org.influxdb</groupId>
+            <artifactId>influxdb-java</artifactId>
+            <version>2.8</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.21</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.21</version>
+        </dependency>
+    
+    </dependencies>
+   
+</project>
\ No newline at end of file
diff --git a/src/clmc-spec/src/main/resources/inputs/host_resource_input b/src/clmc-spec/src/main/resources/inputs/host_resource_input
new file mode 100644
index 0000000..39441c9
--- /dev/null
+++ b/src/clmc-spec/src/main/resources/inputs/host_resource_input
@@ -0,0 +1,9 @@
+# Host Resource metrics
+# 
+# host_resource node_id,sf_inst_id,sf_id,sfc_inst_id,sfc_id,server_id,location cpus,memory,storage <timestamp>
+#
+# NOTE: commas and spaces are significant in the line protocol
+
+host_resource,node_id=1,sf_inst_id=1,sf_id=1,sfc_inst_id=1,sfc_id=1,server_id=1,location="Bristol_DC" cpus=16,memory=256,storage=1024 1513778835385000000
+host_resource,node_id=1,sf_inst_id=2,sf_id=1,sfc_inst_id=1,sfc_id=1,server_id=1,location="Bristol_DC" cpus=16,memory=256,storage=1024 1513778836385000000
+host_resource,node_id=1,sf_inst_id=3,sf_id=1,sfc_inst_id=1,sfc_id=1,server_id=2,location="Bristol_DC" cpus=8,memory=128,storage=1024 1513778837385000000
\ No newline at end of file
diff --git a/src/clmc-spec/src/main/resources/monSpecTestConfig.properties b/src/clmc-spec/src/main/resources/monSpecTestConfig.properties
new file mode 100644
index 0000000..54042b3
--- /dev/null
+++ b/src/clmc-spec/src/main/resources/monSpecTestConfig.properties
@@ -0,0 +1,8 @@
+# Test settings
+clearDBOnExit=false
+
+# InfluxDB
+influxDB_EP=http://localhost:8086
+influxDB_UN=root
+influxDB_PW=root
+influxDB_DB=clmcTestDB
diff --git a/src/clmc-spec/src/main/resources/queries/host_resource_query b/src/clmc-spec/src/main/resources/queries/host_resource_query
new file mode 100644
index 0000000..45c8c16
--- /dev/null
+++ b/src/clmc-spec/src/main/resources/queries/host_resource_query
@@ -0,0 +1,4 @@
+# Host Resource Basic queries
+
+SELECT "node_id", "cpus", "memory", "storage" FROM "clmcTestDB"."autogen"."host_resource"
+
diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java
new file mode 100644
index 0000000..26888bc
--- /dev/null
+++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java
@@ -0,0 +1,72 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// © University of Southampton IT Innovation Centre, 2017
+//
+// Copyright in this software belongs to University of Southampton
+// IT Innovation Centre of Gamma House, Enterprise Road, 
+// Chilworth Science Park, Southampton, SO16 7NS, UK.
+//
+// This software may not be used, sold, licensed, transferred, copied
+// or reproduced in whole or in part in any manner or form or in or
+// on any media by any person other than in accordance with the terms
+// of the Licence Agreement supplied with the software, or otherwise
+// without the prior written consent of the copyright owners.
+//
+// This software is distributed WITHOUT ANY WARRANTY, without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE, except where stated in the Licence Agreement supplied with
+// the software.
+//
+//      Created By :            Simon Crowle
+//      Created Date :          19/12/2017
+//      Created for Project :   FLAME
+//
+/////////////////////////////////////////////////////////////////////////
+package uk.ac.soton.innovation.flame.clmc.monspec.test;
+
+import uk.ac.soton.innovation.flame.clmc.monspec.test.base.TestSuiteBase;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+
+
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    uk.ac.soton.innovation.flame.clmc.monspec.test.BasicInputTest.class,
+    uk.ac.soton.innovation.flame.clmc.monspec.test.BasicQueryTest.class
+})
+public class AlphaTestSuite extends TestSuiteBase 
+{
+
+    @BeforeClass
+    public static void setUpClass() throws Exception 
+    { 
+        TestSuiteBase.setUpClass(); 
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception 
+    {
+        TestSuiteBase.tearDownClass();
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception
+    {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception
+    {
+        super.setUp();
+    }
+    
+}
diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java
new file mode 100644
index 0000000..662b0be
--- /dev/null
+++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java
@@ -0,0 +1,77 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// © University of Southampton IT Innovation Centre, 2017
+//
+// Copyright in this software belongs to University of Southampton
+// IT Innovation Centre of Gamma House, Enterprise Road, 
+// Chilworth Science Park, Southampton, SO16 7NS, UK.
+//
+// This software may not be used, sold, licensed, transferred, copied
+// or reproduced in whole or in part in any manner or form or in or
+// on any media by any person other than in accordance with the terms
+// of the Licence Agreement supplied with the software, or otherwise
+// without the prior written consent of the copyright owners.
+//
+// This software is distributed WITHOUT ANY WARRANTY, without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE, except where stated in the Licence Agreement supplied with
+// the software.
+//
+//      Created By :            Simon Crowle
+//      Created Date :          19/12/2017
+//      Created for Project :   FLAME
+//
+/////////////////////////////////////////////////////////////////////////
+package uk.ac.soton.innovation.flame.clmc.monspec.test;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import uk.ac.soton.innovation.flame.clmc.monspec.test.base.BaseTest;
+
+
+
+/**
+ * BasicInputTest class tests inputting data series into InfluxDB
+ * 
+ * IMPORTANT NOTE: for test automation under Maven, ensure your classes are labelled either
+ * Test* or *Test. Not doing this means Maven will miss this test class.
+ */
+public class BasicInputTest extends BaseTest
+{
+    
+    public BasicInputTest() 
+    {}
+    
+    @BeforeClass
+    public static void setUpClass() 
+    {}
+    
+    @AfterClass
+    public static void tearDownClass()
+    {}
+    
+    @Before
+    @Override
+    public void setUp()
+    {
+        super.setUp();
+    }
+    
+    @After
+    @Override
+    public void tearDown()
+    {
+        super.tearDown();
+    }
+
+    @Test
+    public void testInputSeries()
+    {
+        writeProtocolLines( "host_resource_input" );
+        
+        logger.info( "Completed Test Input Series" );
+    }
+}
diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java
new file mode 100644
index 0000000..862dffa
--- /dev/null
+++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java
@@ -0,0 +1,75 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// © University of Southampton IT Innovation Centre, 2017
+//
+// Copyright in this software belongs to University of Southampton
+// IT Innovation Centre of Gamma House, Enterprise Road, 
+// Chilworth Science Park, Southampton, SO16 7NS, UK.
+//
+// This software may not be used, sold, licensed, transferred, copied
+// or reproduced in whole or in part in any manner or form or in or
+// on any media by any person other than in accordance with the terms
+// of the Licence Agreement supplied with the software, or otherwise
+// without the prior written consent of the copyright owners.
+//
+// This software is distributed WITHOUT ANY WARRANTY, without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE, except where stated in the Licence Agreement supplied with
+// the software.
+//
+//      Created By :            Simon Crowle
+//      Created Date :          20-Dec-2017
+//      Created for Project :   FLAME
+//
+/////////////////////////////////////////////////////////////////////////
+
+package uk.ac.soton.innovation.flame.clmc.monspec.test;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import uk.ac.soton.innovation.flame.clmc.monspec.test.base.BaseTest;
+
+
+
+/**
+ * BasicQueryTest class tests querying InfluxDB using data from BasicInputTest
+ * 
+ * IMPORTANT NOTE: for test automation under Maven, ensure your classes are labelled either
+ * Test* or *Test. Not doing this means Maven will miss this test class.
+ */
+public class BasicQueryTest extends BaseTest
+{
+    public BasicQueryTest() 
+    {}
+    
+    @BeforeClass
+    public static void setUpClass() 
+    {}
+    
+    @AfterClass
+    public static void tearDownClass()
+    {}
+    
+    @Before
+    @Override
+    public void setUp()
+    {
+        super.setUp();
+    }
+    
+    @After
+    @Override
+    public void tearDown()
+    {
+        super.tearDown();
+    }
+
+    @Test
+    public void querySoloSeries()
+    {
+        executeQueries( "host_resource_query", true );
+    }
+}
diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java
new file mode 100644
index 0000000..4b3c724
--- /dev/null
+++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java
@@ -0,0 +1,137 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// © University of Southampton IT Innovation Centre, 2017
+//
+// Copyright in this software belongs to University of Southampton
+// IT Innovation Centre of Gamma House, Enterprise Road, 
+// Chilworth Science Park, Southampton, SO16 7NS, UK.
+//
+// This software may not be used, sold, licensed, transferred, copied
+// or reproduced in whole or in part in any manner or form or in or
+// on any media by any person other than in accordance with the terms
+// of the Licence Agreement supplied with the software, or otherwise
+// without the prior written consent of the copyright owners.
+//
+// This software is distributed WITHOUT ANY WARRANTY, without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE, except where stated in the Licence Agreement supplied with
+// the software.
+//
+//      Created By :            Simon Crowle
+//      Created Date :          19/12/2017
+//      Created for Project :   FLAME
+//
+/////////////////////////////////////////////////////////////////////////
+package uk.ac.soton.innovation.flame.clmc.monspec.test.base;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.influxdb.InfluxDB;
+import org.junit.After;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * BaseTest is an abstract class used to set up InfluxDB resource before
+ * executing tests proper. Derive from this class and use protected methods.
+ */
+public abstract class BaseTest 
+{
+    protected Logger   logger = LoggerFactory.getLogger( BaseTest.class );
+    protected InfluxDB influxDB;
+    
+    public BaseTest() 
+    {}
+    
+    @BeforeClass
+    public static void setUpClass() 
+    {}
+    
+    @AfterClass
+    public static void tearDownClass() 
+    {}
+    
+    @Before
+    public void setUp()
+    {
+        try
+        {
+            // Create test database if it does not already exist
+            influxDB = InfluxUtility.getInfluxDBConnection();
+            String dbName = InfluxUtility.getTestConfig().getProperty( "influxDB_DB" );
+            
+            if ( !influxDB.databaseExists(dbName) )
+                influxDB.createDatabase( dbName );
+            
+            influxDB.setDatabase( dbName );
+        }
+        catch ( Exception ex )
+        {
+            logger.error( "Failed to get InfluxDB connection: " + ex.getMessage() );
+            assertTrue( false );
+        }
+    }
+    
+    @After
+    public void tearDown() 
+    {}
+    
+    // Protected methods -------------------------------------------------------
+    /**
+     * Attempts to write InfluxDB Line Protocol lines to InfluxDB
+     * 
+     * @param seriesLabel - label of input resource (see /src/main/resources/inputs
+     */
+    protected void writeProtocolLines( String seriesLabel )
+    {
+        try
+        {
+            // Get line protocols (will throw if non-existent or empty)
+            List<String> protocols = InfluxUtility.getInputSeries( seriesLabel );
+            
+            for ( String lp : protocols )
+                influxDB.write( lp );
+        }
+        catch ( Exception ex )
+        {
+            String err = "Failed writing protocol lines: " + ex.getMessage();
+            logger.error( err );
+            
+            assertTrue( false );
+        }
+    }
+    
+    /**
+     * Attempts to execute one or more queries with InfluxDB
+     * 
+     * @param queryLabel - name of query input file resource (see /src/main/resources/queries)
+     * @param printOut   - boolean to indicate whether results are logged out to console
+     * @return           - Array of query results (per series set)
+     */
+    protected List<List<String>> executeQueries( String queryLabel, boolean printOut )
+    {
+        List<List<String>> uberResult = new ArrayList<>();
+        
+        try
+        {
+            // Get test query set (will throw if non-existent or empty)
+            List<String> queries = InfluxUtility.getQueries( queryLabel );
+            
+            for ( String q : queries )
+                uberResult.add(InfluxUtility.executeQuery(q,printOut) );
+        }
+        catch ( Exception ex )
+        {
+            String err = "Failed to execute queries: " + queryLabel;
+            logger.error( err );
+            
+            assertTrue( false );
+        }
+        
+        return uberResult;
+    }
+}
diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java
new file mode 100644
index 0000000..0204b09
--- /dev/null
+++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java
@@ -0,0 +1,309 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// © University of Southampton IT Innovation Centre, 2017
+//
+// Copyright in this software belongs to University of Southampton
+// IT Innovation Centre of Gamma House, Enterprise Road, 
+// Chilworth Science Park, Southampton, SO16 7NS, UK.
+//
+// This software may not be used, sold, licensed, transferred, copied
+// or reproduced in whole or in part in any manner or form or in or
+// on any media by any person other than in accordance with the terms
+// of the Licence Agreement supplied with the software, or otherwise
+// without the prior written consent of the copyright owners.
+//
+// This software is distributed WITHOUT ANY WARRANTY, without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE, except where stated in the Licence Agreement supplied with
+// the software.
+//
+//      Created By :            Simon Crowle
+//      Created Date :          19-Dec-2017
+//      Created for Project :   FLAME
+//
+/////////////////////////////////////////////////////////////////////////
+
+package uk.ac.soton.innovation.flame.clmc.monspec.test.base;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import org.influxdb.InfluxDB;
+import org.influxdb.InfluxDBFactory;
+import org.influxdb.dto.Query;
+import org.influxdb.dto.QueryResult;
+import org.influxdb.dto.QueryResult.Result;
+import org.influxdb.dto.QueryResult.Series;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ * Helper class for accessing InfluxDB and processing results 
+ */
+public class InfluxUtility
+{
+    private static Logger     logger = LoggerFactory.getLogger(InfluxUtility.class );
+    private static Properties testConfig;
+    private static InfluxDB   influxDB;
+    
+    
+    public static Properties getTestConfig()
+    {
+        if ( testConfig == null )
+        {
+            InputStream is = null;
+            try
+            {
+                testConfig = new Properties();
+                
+                is = ClassLoader.class.getResourceAsStream( "/monSpecTestConfig.properties" );
+                
+                if ( is != null )
+                {
+                    testConfig.load( is );
+                    logger.info( "Found test configuration OK" );
+                }
+                else
+                    throw new Exception( "Failed to find test configuration" );
+            }
+            catch ( Exception ex )
+            {
+                String err = "Could not find test configuration: " + ex.getMessage();
+                logger.error( err );
+            }
+            finally
+            {
+                try
+                {
+                    if ( is != null )
+                        is.close();
+                }
+                catch ( Exception ex )
+                { logger.warn( "Had problems closing resource input stream" ); }
+            }
+        }
+        
+        return testConfig;
+    }
+    
+    public static InfluxDB getInfluxDBConnection() throws Exception
+    {
+        InfluxDB result = null;
+        
+        try
+        {
+            if ( influxDB == null )
+            {
+                Properties config = getTestConfig();
+                logger.info( "Trying to connect to Influx: " + config.getProperty("influxDB_EP") );
+        
+                influxDB = InfluxDBFactory.connect( config.getProperty("influxDB_EP"), 
+                                                    config.getProperty("influxDB_UN"),
+                                                    config.getProperty("influxDB_PW") );
+                
+                result = influxDB;
+                logger.info( "Influx connection OK" );
+            }
+            else
+                result = influxDB;
+        }
+        catch ( Exception ex )
+        {
+            String err = "Failed to load InfluxDB configuration: " + ex.getMessage();
+            logger.error( err, ex );
+            
+            throw new Exception( err );
+        }
+        
+        return result;
+    }
+    
+    public static List<String> getInputSeries( String label ) throws Exception
+    {
+        // Safety first
+        if ( label == null || label.isEmpty() )
+            throw new Exception( "Could not get input series: label invalid" );
+        
+        return tryGetTestFile( "/inputs/" + label );
+    }
+    
+    public static List<String> getQueries( String label ) throws Exception
+    {
+        // Safety first
+        if ( label == null || label.isEmpty() )
+            throw new Exception( "Could not get queries: label invalid" );
+        
+        return tryGetTestFile( "/queries/" + label );
+    }
+    
+    public static List<String> executeQuery( String query, boolean printOut ) throws Exception
+    {
+        List<String> result = null;
+        
+        // Safety first
+        if ( query == null || query.isEmpty() )
+            throw new Exception( "Could not execute query: invalid input param" );
+        
+        if ( influxDB == null )
+            throw new Exception( "Could not execute query: no InfluxDB connection" );
+        
+        // Query it
+        Query fluxQuery = new Query( query, testConfig.getProperty("influxDB_DB") );
+        QueryResult qResult = influxDB.query( fluxQuery, TimeUnit.NANOSECONDS );
+        
+        // Safety
+        if ( qResult == null )
+            throw new Exception( "Got null InfluxDB query result for: " + query );
+        else
+            result = parseFluxQueryResult( qResult );
+        
+        if ( printOut )
+        {
+            String rOut = "";
+            for ( String line : result )
+                rOut += line + "\n";
+            
+            logger.info( rOut );
+        }
+        
+        return result;
+    }
+    
+    // Private methods ---------------------------------------------------------
+    private static List<String> tryGetTestFile( String path ) throws Exception
+    {
+        List<String> result = new ArrayList<>();
+        
+        // Safety first
+        if ( path == null || path.isEmpty() )
+            throw new Exception( "Could not get test file: path invalid" );
+        
+        InputStream    is = null;
+        BufferedReader br = null;
+        
+        try
+        {
+            // Load resource as stream
+            is = ClassLoader.class.getResourceAsStream( path );
+            
+            if ( is != null )
+            {
+                // Read into list
+                br = new BufferedReader( new InputStreamReader(is) );
+                String nextLine;
+                
+                while( (nextLine = br.readLine()) != null )
+                {
+                    if ( !nextLine.startsWith("#") && !nextLine.isEmpty() )
+                        result.add( nextLine );
+                }
+            }
+            else
+                throw new Exception( "Failed to find test file" );
+        }
+        catch ( Exception ex )
+        {
+            String err = "Could not find test file: " + ex.getMessage();
+            logger.error( err );
+        }
+        // Clean up
+        finally
+        {
+            try
+            {
+                if ( br != null )
+                    br.close();
+                
+                if ( is != null )
+                    is.close();
+            }
+            catch ( Exception ex )
+            { logger.warn( "Had problems closing resource input stream: " + ex.getMessage() ); }
+        }
+        
+        // Final safety check
+        if ( result.isEmpty() )
+            throw new Exception( "Found no lines in test file" );
+        
+        return result;
+    }
+    
+    /**
+     * This method simply parses the result of the query and formats as CSV (tab delimited)
+     * 
+     * @param queryResult  - The structured query result returned by InfluxDB
+     * @return             - A list of rows in Line Protocol format
+     */
+    private static List<String> parseFluxQueryResult( QueryResult queryResult )
+    {
+        List<String> result = new ArrayList<>();
+        
+        for ( Result res : queryResult.getResults() )
+        {
+            for ( Series series : res.getSeries() )
+            {
+                // Result series name
+                result.add( series.getName() );
+                
+                // TODO: Unclear what the tags collection is for at the moment
+                
+                // Column titles
+                String colNames = "";
+                int volInd = 0;
+                for ( String colName : series.getColumns() )
+                {
+                    // Special formatting for time value
+                    if ( volInd == 0 )
+                        colNames += colName + "\t\t\t";
+                    else
+                        colNames += colName + "\t";
+                    
+                    ++volInd;
+                }
+                    
+                
+                colNames = colNames.substring( 0, colNames.length() -1 ); // Trim last comma
+                result.add( colNames );
+                
+                // Results
+                for ( List<Object> rowList : series.getValues() )
+                {
+                    String valList = "";
+                    volInd = 0;
+                    
+                    for ( Object val : rowList )
+                    {
+                        // Special formatting for time value
+                        // Note: some nasty rounding errors in nanosecond number component
+                        // for now round down to millisecond
+                        if ( volInd == 0 )
+                        {
+                            Double tD = (Double) val;
+                            Double msFrac = tD / 100000;
+                            Long tL = msFrac.longValue();
+                            tL *= 100000;
+                            
+                            valList += tL.toString() + "\t";
+                            
+                        }
+                        else
+                            valList += val.toString() + "\t";
+                        
+                        ++volInd;
+                    }
+                    
+                    valList = valList.substring( 0, valList.length() -1 ); // Trim last comma
+                    result.add( valList );
+                }
+            }
+        }
+        
+        return result;
+    }
+}
diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java
new file mode 100644
index 0000000..965f111
--- /dev/null
+++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java
@@ -0,0 +1,94 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// © University of Southampton IT Innovation Centre, 2017
+//
+// Copyright in this software belongs to University of Southampton
+// IT Innovation Centre of Gamma House, Enterprise Road, 
+// Chilworth Science Park, Southampton, SO16 7NS, UK.
+//
+// This software may not be used, sold, licensed, transferred, copied
+// or reproduced in whole or in part in any manner or form or in or
+// on any media by any person other than in accordance with the terms
+// of the Licence Agreement supplied with the software, or otherwise
+// without the prior written consent of the copyright owners.
+//
+// This software is distributed WITHOUT ANY WARRANTY, without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE, except where stated in the Licence Agreement supplied with
+// the software.
+//
+//      Created By :            Simon Crowle
+//      Created Date :          19/12/2017
+//      Created for Project :   FLAME
+//
+/////////////////////////////////////////////////////////////////////////
+package uk.ac.soton.innovation.flame.clmc.monspec.test.base;
+
+import java.util.Properties;
+import org.influxdb.InfluxDB;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({})
+/**
+ * TestSuiteBase is an abstract class used to set up Influx test suites. Derive 
+ * from this class to create InfluxDB test suites
+ */
+public abstract class TestSuiteBase
+{
+    private static Logger logger = LoggerFactory.getLogger( TestSuiteBase.class );
+    
+    @BeforeClass
+    public static void setUpClass() throws Exception 
+    {
+        InfluxUtility.getInfluxDBConnection(); // throws if connection fails
+        
+        destroyTestDatabase();
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception
+    {
+        Properties config = InfluxUtility.getTestConfig();
+        
+        if ( "true".equals( config.getProperty("clearDBOnExit")) )
+            destroyTestDatabase();
+    }
+
+    @Before
+    public void setUp() throws Exception
+    {
+        
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+        
+    }
+    
+    // Private methods ---------------------------------------------------------
+    private static void destroyTestDatabase()
+    {
+        try
+        {
+            String dbName = InfluxUtility.getTestConfig().getProperty( "influxDB_DB" );
+            InfluxDB influxDB = InfluxUtility.getInfluxDBConnection();
+            
+            if ( influxDB.databaseExists( dbName ) )
+                influxDB.deleteDatabase( dbName );
+        }
+        catch ( Exception ex )
+        {
+            logger.error( ex.getMessage() );
+        }
+    }
+}
-- 
GitLab