You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2018/08/23 22:36:46 UTC

[incubator-openwhisk-runtime-go] branch master updated: Merging to facilitate more work on unit testing

This is an automated email from the ASF dual-hosted git repository.

rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-runtime-go.git


The following commit(s) were added to refs/heads/master by this push:
     new 307db8a  Merging to facilitate more work on unit testing
307db8a is described below

commit 307db8a1002083e189a68cb20d93a36704fbc263
Author: Michele Sciabarra <mi...@sciabarra.com>
AuthorDate: Fri Aug 24 00:36:43 2018 +0200

    Merging to facilitate more work on unit testing
---
 .gitignore                                         |   1 +
 gradle/wrapper/gradle-wrapper.properties           |   2 +-
 settings.gradle                                    |   2 +
 settings.gradle => tests/build.gradle              |  38 ++--
 .../ActionLoopContainerTests.scala                 |  92 ++++++++
 .../ActionLoopGoContainerTests.scala               | 248 +++++++++++++++++++++
 .../actionContainers/GoResourceHelpers.scala       | 207 +++++++++++++++++
 tools/travis/build.sh                              |  17 +-
 tools/travis/setup.sh                              |  10 +-
 tools/travis/test.sh                               |   6 +-
 10 files changed, 597 insertions(+), 26 deletions(-)

diff --git a/.gitignore b/.gitignore
index 16ffbc9..793f57e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 .gradle/
 .gogradle/
 *.log
+tests/build/
 
 # Dependencies
 vendor/
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d7cc33f..abd704b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -5,4 +5,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/settings.gradle b/settings.gradle
index 4a24586..1892607 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+include 'tests'
+
 include 'actionProxyLoop'
 include 'golang1.10'
 
diff --git a/settings.gradle b/tests/build.gradle
similarity index 52%
copy from settings.gradle
copy to tests/build.gradle
index 4a24586..1ae5a4b 100644
--- a/settings.gradle
+++ b/tests/build.gradle
@@ -15,22 +15,30 @@
  * limitations under the License.
  */
 
-include 'actionProxyLoop'
-include 'golang1.10'
+apply plugin: 'scala'
+apply plugin: 'eclipse'
+compileTestScala.options.encoding = 'UTF-8'
 
-rootProject.name = 'runtime-golang'
+repositories {
+    mavenCentral()
+    mavenLocal()
+}
 
-gradle.ext.openwhisk = [
-        version: '1.0.0-SNAPSHOT'
-]
+tasks.withType(Test) {
+    testLogging {
+        events "passed", "skipped", "failed"
+        showStandardStreams = true
+        exceptionFormat = 'full'
+    }
+    outputs.upToDateWhen { false } // force tests to run every time
+}
 
-gradle.ext.scala = [
-    version: '2.11.8',
-    compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
-    version: '1.5.0',
-    config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
+dependencies {
+    compile "org.scala-lang:scala-library:${gradle.scala.version}"
+    compile "org.apache.openwhisk:openwhisk-tests:${gradle.openwhisk.version}:tests"
+    compile "org.apache.openwhisk:openwhisk-tests:${gradle.openwhisk.version}:test-sources"
+}
 
+tasks.withType(ScalaCompile) {
+    scalaCompileOptions.additionalParameters = gradle.scala.compileFlags
+}
diff --git a/tests/src/test/scala/runtime/actionContainers/ActionLoopContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/ActionLoopContainerTests.scala
new file mode 100644
index 0000000..ef11078
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/ActionLoopContainerTests.scala
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+//import java.util.concurrent.TimeoutException
+import actionContainers.{ActionContainer, ActionProxyContainerTestUtils}
+import actionContainers.ActionContainer.withContainer
+import common.WskActorSystem
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+//import spray.json.JsNumber
+import spray.json.{JsObject, JsString}
+//import spray.json.JsBoolean
+
+@RunWith(classOf[JUnitRunner])
+class ActionLoopContainerTests extends ActionProxyContainerTestUtils with WskActorSystem {
+
+  import GoResourceHelpers._
+
+  // "example" is the image build by /sdk/docker
+  def withActionLoopContainer(code: ActionContainer => Unit) = withContainer("actionloop")(code)
+
+  behavior of "actionloop"
+
+  def shCodeHello(main: String) = Seq(
+    Seq(main) ->
+      s"""#!/bin/bash
+         |while read line
+         |do
+         |   name="$$(echo $$line | jq -r .name)"
+         |   if test "$$name" == ""
+         |   then exit
+         |   fi
+         |   echo "name=$$name"
+         |   hello="Hello, $$name"
+         |   echo '{"${main}":"'$$hello'"}' >&3
+         |done
+         |""".stripMargin
+  )
+
+  private def helloMsg(name: String = "Demo") =
+    runPayload(JsObject("name" -> JsString(name)))
+
+  private def okMsg(key: String, value: String) =
+    200 -> Some(JsObject(key -> JsString(value)))
+
+
+  it should "run sample with init that does nothing" in {
+    val (out, err) = withActionLoopContainer { c =>
+      c.init(JsObject())._1 should be(200)
+      c.run(JsObject())._1 should be(400)
+    }
+  }
+
+
+  it should "deploy a shell script" in {
+    val script = shCodeHello("main")
+    val mesg = ExeBuilder.mkBase64Src(script, "main")
+    withActionLoopContainer {
+      c =>
+        val payload = initPayload(mesg)
+        c.init(payload)._1 should be(200)
+        c.run(helloMsg()) should be(okMsg("main", "Hello, Demo"))
+    }
+  }
+
+  it should "deploy a zip based script" in {
+    val scr = ExeBuilder.mkBase64SrcZip(shCodeHello("main"), "main")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(scr))._1 should be(200)
+        c.run(helloMsg()) should be(okMsg("main", "Hello, Demo"))
+    }
+  }
+ }
+
diff --git a/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala
new file mode 100644
index 0000000..e4c7320
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+//import java.util.concurrent.TimeoutException
+import actionContainers.{ActionContainer, ActionProxyContainerTestUtils}
+import actionContainers.ActionContainer.withContainer
+import common.WskActorSystem
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+//import spray.json.JsNumber
+//import spray.json.JsBoolean
+import spray.json.{JsObject, JsString}
+
+@RunWith(classOf[JUnitRunner])
+class ActionLoopGoContainerTests extends ActionProxyContainerTestUtils with WskActorSystem {
+
+  import GoResourceHelpers._
+
+  val goCompiler = "actionloop-golang-v1.10"
+  val image = goCompiler
+
+  def withActionLoopContainer(code: ActionContainer => Unit) = withContainer(image)(code)
+
+  behavior of image
+
+
+  private def checkresponse(res: Option[JsObject], args: JsObject = JsObject()) = {
+    res shouldBe defined
+    res.get.fields("error") shouldBe JsString("no action defined yet")
+    //res.get.fields("args") shouldBe args
+  }
+
+  private def goCodeHello(file: String, main: String) = Seq(
+    Seq(s"${file}.go") ->
+      s"""
+         |package action
+         |
+         |import (
+         |	"encoding/json"
+         |	"fmt"
+         |)
+         |
+         |func ${main}(event json.RawMessage) (json.RawMessage, error) {
+         |	var obj map[string]interface{}
+         |	json.Unmarshal(event, &obj)
+         |	name, ok := obj["name"].(string)
+         |	if !ok {
+         |		name = "Stranger"
+         |	}
+         |	fmt.Printf("name=%s\\n", name)
+         |	msg := map[string]string{"${file}-${main}": ("Hello, " + name + "!")}
+         |	return json.Marshal(msg)
+         |}
+         |
+          """.stripMargin
+  )
+
+  private def helloMsg(name: String = "Demo") =
+    runPayload(JsObject("name" -> JsString(name)))
+
+  private def okMsg(key: String, value: String) =
+    200 -> Some(JsObject(key -> JsString(value)))
+
+
+  it should "run sample with init that does nothing" in {
+    val (out, err) = withActionLoopContainer { c =>
+      c.init(JsObject())._1 should be(200)
+      c.run(JsObject())._1 should be(400)
+    }
+  }
+
+  it should "accept a binary main" in {
+    val exe = ExeBuilder.mkBase64Exe(
+      goCompiler, goCodeHello("main", "Main"), "main")
+
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(exe))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("main-Main", "Hello, Demo!"))
+    }
+  }
+
+  //def pr(x: Any) = { println(x) ; x}
+
+  it should "build and run a go main zipped exe" in {
+    val zip = ExeBuilder.mkBase64Zip(
+      goCompiler, goCodeHello("main", "Main"), "main")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(zip))._1 should be(200)
+        c.run(helloMsg()) should be(okMsg("main-Main", "Hello, Demo!"))
+    }
+  }
+
+  it should "buid and run a go hello exe " in {
+    val exe = ExeBuilder.mkBase64Exe(
+      goCompiler, goCodeHello("hello", "Hello"), "hello")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(exe, "hello"))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("hello-Hello", "Hello, Demo!"))
+    }
+  }
+
+  it should "build and run a go hello zipped exe" in {
+    val zip = ExeBuilder.mkBase64Zip(
+      goCompiler, goCodeHello("hello", "Hello"), "hello")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(zip, "hello"))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("hello-Hello", "Hello, Demo!"))
+    }
+  }
+
+  val helloSrc =
+    """
+      |package action
+      |
+      |import (
+      |	"encoding/json"
+      |	"fmt"
+      |)
+      |
+      |func Hello(event json.RawMessage) (json.RawMessage, error) {
+      |	var obj struct {
+      |		Name string `json:",omitempty"`
+      |	}
+      |	err := json.Unmarshal(event, &obj)
+      |	if err != nil {
+      |		return nil, err
+      |	}
+      |	name := obj.Name
+      |	if name == "" {
+      |		name = "Stranger"
+      |	}
+      |	fmt.Printf("name=%s\n", name)
+      |	msg := map[string]string{"Hello": ("Hello, " + name + "!")}
+      |	return json.Marshal(msg)
+      |}
+    """.stripMargin
+
+  val mainSrc =
+    """
+      |package action
+      |
+      |import (
+      |	"encoding/json"
+      |	"fmt"
+      |)
+      |
+      |func Main(event json.RawMessage) (json.RawMessage, error) {
+      |	var obj map[string]interface{}
+      |	json.Unmarshal(event, &obj)
+      |	name, ok := obj["name"].(string)
+      |	if !ok {
+      |		name = "Stranger"
+      |	}
+      |	fmt.Printf("name=%s\n", name)
+      |	msg := map[string]string{"Main": ("Hello, " + name + "!")}
+      |	return json.Marshal(msg)
+      |}
+    """.stripMargin
+
+  it should "deploy a src main action " in {
+    var src = ExeBuilder.mkBase64Src(Seq(
+      Seq("main") -> mainSrc
+    ), "main")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(src))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("Main", "Hello, Demo!"))
+    }
+  }
+
+  it should "deploy a src hello action " in {
+    var src = ExeBuilder.mkBase64Src(Seq(
+      Seq("hello") -> helloSrc
+    ), "hello")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(src, "hello"))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("Hello", "Hello, Demo!"))
+    }
+  }
+
+  it should "deploy a zip main src action" in {
+    var src = ExeBuilder.mkBase64SrcZip(Seq(
+      Seq("main.go") -> mainSrc
+    ), "main")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(src))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("Main", "Hello, Demo!"))
+    }
+  }
+
+  it should "deploy a zip main src subdir action" in {
+    var src = ExeBuilder.mkBase64SrcZip(Seq(
+      Seq("action", "main.go") -> mainSrc
+    ), "main")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(src))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("Main", "Hello, Demo!"))
+    }
+  }
+
+  it should "deploy a zip src hello action " in {
+    var src = ExeBuilder.mkBase64SrcZip(Seq(
+      Seq("hello.go") -> helloSrc
+    ), "hello")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(src, "hello"))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("Hello", "Hello, Demo!"))
+    }
+  }
+
+
+  it should "deploy a zip src hello action in subdir" in {
+    var src = ExeBuilder.mkBase64SrcZip(Seq(
+      Seq("action", "hello.go") -> helloSrc
+    ), "hello")
+    withActionLoopContainer {
+      c =>
+        c.init(initPayload(src, "hello"))._1 shouldBe (200)
+        c.run(helloMsg()) should be(okMsg("Hello", "Hello, Demo!"))
+    }
+  }
+}
+
diff --git a/tests/src/test/scala/runtime/actionContainers/GoResourceHelpers.scala b/tests/src/test/scala/runtime/actionContainers/GoResourceHelpers.scala
new file mode 100644
index 0000000..c001f22
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/GoResourceHelpers.scala
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+import java.io.File
+import java.net.URI
+import java.nio.file._
+import java.nio.charset.StandardCharsets
+import java.nio.file.attribute.BasicFileAttributes
+
+import collection.JavaConverters._
+import actionContainers.ResourceHelpers
+
+object GoResourceHelpers {
+
+
+  /** Create a temporary directory in your /tmp directory.
+    *
+    * This is needed to use docker volume mounts.
+    * On mac I need to use the /tmp directory,
+    * because the default folder used by gradle under Mac
+    * is not accessible by default by Docker for Mac
+    *
+    */
+  def tmpDirectoryFile(prefix: String, suffix: String = "") =
+    new File(new File("/tmp", "openwhisk"), prefix+System.currentTimeMillis().toString+suffix)
+
+  def createTmpDirectory(prefix: String, suffix: String = "") = {
+    val tmpDir = tmpDirectoryFile(prefix,suffix)
+    tmpDir.mkdirs()
+    tmpDir.toPath.toAbsolutePath
+  }
+
+  private def makeZipFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".zip")
+
+  private def makeJarFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".jar")
+
+  /**
+    * Compresses all files beyond a directory into a zip file.
+    * Note that Jar files are just zip files.
+    */
+  private def makeArchiveFromDir(dir: Path, extension: String): Path = {
+    // Any temporary file name for the archive.
+    val arPath = createTmpDirectory("output", extension).toAbsolutePath()
+
+    // We "mount" it as a filesystem, so we can just copy files into it.
+    val dstUri = new URI("jar:" + arPath.toUri().getScheme(), arPath.toAbsolutePath().toString(), null)
+    // OK, that's a hack. Doing this because newFileSystem wants to create that file.
+    arPath.toFile().delete()
+    val fs = FileSystems.newFileSystem(dstUri, Map(("create" -> "true")).asJava)
+
+    // Traversing all files in the bin directory...
+    Files.walkFileTree(
+      dir,
+      new SimpleFileVisitor[Path]() {
+        override def visitFile(path: Path, attributes: BasicFileAttributes) = {
+          // The path relative to the src dir
+          val relPath = dir.relativize(path)
+
+          // The corresponding path in the zip
+          val arRelPath = fs.getPath(relPath.toString())
+
+          // If this file is not top-level in the src dir...
+          if (relPath.getParent() != null) {
+            // ...create the directory structure if it doesn't exist.
+            if (!Files.exists(arRelPath.getParent())) {
+              Files.createDirectories(arRelPath.getParent())
+            }
+          }
+
+          // Finally we can copy that file.
+          Files.copy(path, arRelPath)
+
+          FileVisitResult.CONTINUE
+        }
+      })
+
+    fs.close()
+
+    arPath
+  }
+
+
+  /**
+    * Creates a temporary directory in the home and reproduces the desired file structure
+    * in it. Returns the path of the temporary directory and the path of each
+    * file as represented in it.
+    *
+    * NOTE this is different from writeSourcesToTempDirectory because it uses
+    * a tmp folder in the home directory.
+    *
+    * */
+  private def writeSourcesToHomeTmpDirectory(sources: Seq[(Seq[String], String)]): (Path, Seq[Path]) = {
+    // A temporary directory for the source files.
+    val srcDir = createTmpDirectory("src")
+    val srcAbsPaths = for ((sourceName, sourceContent) <- sources) yield {
+      // The relative path of the source file
+      val srcRelPath = Paths.get(sourceName.head, sourceName.tail: _*)
+      // The absolute path of the source file
+      val srcAbsPath = srcDir.resolve(srcRelPath)
+      // Create parent directories if needed.
+      Files.createDirectories(srcAbsPath.getParent)
+      // Writing contents
+      Files.write(srcAbsPath, sourceContent.getBytes(StandardCharsets.UTF_8))
+
+      srcAbsPath
+    }
+
+    (srcDir, srcAbsPaths)
+  }
+
+  /**
+    * Builds executables using docker images as compilers.
+    * Images are assumed to be able to be run as
+    * docker <image> -v <sources>:/src -v <output>:/out compile <main>
+    * The compiler will read sources from /src and leave the final binary in /out/<main>
+    * <main> is also the name of the main function to be invoked
+    * Implementations available for swift and go
+    */
+  object ExeBuilder {
+
+    private lazy val dockerBin: String = {
+      List("/usr/bin/docker", "/usr/local/bin/docker").find { bin =>
+        new File(bin).isFile
+      }.get // This fails if the docker binary couldn't be located.
+    }
+
+    // prepare sources, then compile them
+    // return the exe File  and the output dir Path
+    private def compile(image: String, sources: Seq[(Seq[String], String)], main: String) = {
+      require(!sources.isEmpty)
+
+      // The absolute paths of the source file
+      val (srcDir, srcAbsPaths) = writeSourcesToHomeTmpDirectory(sources)
+      val src = srcDir.toFile.getAbsolutePath
+
+      // A temporary directory for the destination files.
+      // DO NOT CREATE IT IN ADVANCE or you will get a permission denied
+      val outDir = tmpDirectoryFile("out")
+      val out = outDir.toPath.toAbsolutePath
+
+      // command to compile
+      val exe = new File(outDir, main)
+
+      val cmd = s"${dockerBin} run " +
+        s"-v ${src}:/src " +
+        s"-v ${out}:/out:rw " +
+        s"${image} compile ${main}"
+
+      // compiling
+      import sys.process._
+      cmd.!
+
+      // result
+      exe -> outDir.toPath
+
+    }
+
+    def mkBase64Exe(image: String, sources: Seq[(Seq[String], String)], main: String) = {
+      val (exe, dir) = compile(image, sources, main)
+      //println(s"exe=${exe.getAbsolutePath}")
+      ResourceHelpers.readAsBase64(exe.toPath)
+    }
+
+    def mkBase64Zip(image: String, sources: Seq[(Seq[String], String)], main: String) = {
+      val (exe, dir) = compile(image, sources, main)
+      val archive = makeZipFromDir(dir)
+      //println(s"zip=${archive.toFile.getAbsolutePath}")
+      ResourceHelpers.readAsBase64(archive)
+    }
+
+    def mkBase64Src(sources: Seq[(Seq[String], String)], main: String) = {
+      val (srcDir, srcAbsPaths) = writeSourcesToHomeTmpDirectory(sources)
+      val file = new File(srcDir.toFile, main)
+      //println(file)
+      ResourceHelpers.readAsBase64(file.toPath)
+    }
+
+    def mkBase64SrcZip(sources: Seq[(Seq[String], String)], main: String) = {
+      val (srcDir, srcAbsPaths) = writeSourcesToHomeTmpDirectory(sources)
+      //println(srcDir)
+      val archive = makeZipFromDir(srcDir)
+      ResourceHelpers.readAsBase64(archive)
+    }
+  }
+
+  def writeFile(name: String, body: String): Unit = {
+    val fw = new java.io.FileWriter(name)
+    fw.write(body)
+    fw.close
+  }
+}
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index 037256d..450540d 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -27,16 +27,21 @@ UTILDIR="$ROOTDIR/../incubator-openwhisk-utilities"
 
 export OPENWHISK_HOME=$WHISKDIR
 
-IMAGE_PREFIX="testing"
-
 # run scancode using the ASF Release configuration
 cd $UTILDIR
 scancode/scanCode.py --config scancode/ASF-Release.cfg $ROOTDIR
 
-cd $ROOTDIR
+# Build OpenWhisk deps before we run tests
+cd $WHISKDIR
+TERM=dumb ./gradlew install
+# Mock file (works around bug upstream)
+echo "openwhisk.home=$WHISKDIR" > whisk.properties
+echo "vcap.services.file=" >> whisk.properties
 
 # Build/Compile go
-TERM=dumb ./gradlew build
+cd $ROOTDIR
+# compile without testing as we need docker images first
+TERM=dumb ./gradlew build -x test
+# ok now you can build docker images using the compiled proxy
+TERM=dumb ./gradlew distDocker
 
-# Build runtime image
-TERM=dumb ./gradlew distDocker -PdockerImagePrefix=${IMAGE_PREFIX}
diff --git a/tools/travis/setup.sh b/tools/travis/setup.sh
index 17e7285..fa7ffeb 100755
--- a/tools/travis/setup.sh
+++ b/tools/travis/setup.sh
@@ -18,6 +18,10 @@
 
 set -e
 
+# add realpath
+sudo apt-get -y update
+sudo apt-get -y install realpath
+
 # Build script for Travis-CI.
 
 SCRIPTDIR=$(cd $(dirname "$0") && pwd)
@@ -28,5 +32,7 @@ HOMEDIR="$SCRIPTDIR/../../../"
 cd $HOMEDIR
 git clone https://github.com/apache/incubator-openwhisk-utilities.git
 
-# add realpath
-sudo apt-get -y update && sudo apt-get -y install realpath
+# clone main openwhisk repo. for testing purposes
+git clone --depth=1 https://github.com/apache/incubator-openwhisk.git openwhisk
+cd openwhisk
+./tools/travis/setup.sh
diff --git a/tools/travis/test.sh b/tools/travis/test.sh
index 81ab042..2534d83 100755
--- a/tools/travis/test.sh
+++ b/tools/travis/test.sh
@@ -22,9 +22,11 @@ set -ex
 
 SCRIPTDIR=$(cd $(dirname "$0") && pwd)
 ROOTDIR="$SCRIPTDIR/../.."
+WHISKDIR="$ROOTDIR/../openwhisk"
 
+export OPENWHISK_HOME=$WHISKDIR
 cd ${ROOTDIR}
-TERM=dumb ./gradlew test --info
-
+TERM=dumb ./gradlew :tests:test
 
+TERM=dumb ./gradlew test --info