Update python runtime to include its own test artifacts. (#77)

* Remove python 2 tests, no longer maintained.
* Lookup test artifacts relative to project resources.
* Build test artifacts before tests.
* Build action loop proxy from source to get bug fix since last release
diff --git a/.gitignore b/.gitignore
index f65f024..4f7e0aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,12 +61,7 @@
 ansible/roles/nginx/files/*cert.pem
 
 # .zip files must be explicited whitelisted
+.built
 *.zip
-!tests/dat/actions/blackbox.zip
-!tests/dat/actions/helloSwift.zip
-!tests/dat/actions/python.zip
-!tests/dat/actions/python2_virtualenv.zip
-!tests/dat/actions/python3_virtualenv.zip
-!tests/dat/actions/python_virtualenv_dir.zip
-!tests/dat/actions/python_virtualenv_name.zip
-!tests/dat/actions/zippedaction.zip
+tests/**/python_virtualenv/virtualenv/
+tests/**/python_virtualenv_invalid_main/virtualenv/
diff --git a/.travis.yml b/.travis.yml
index 2f566ef..2184acf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,7 +18,11 @@
 sudo: required
 dist: xenial
 jdk: openjdk8
-language: java
+language:
+  - python
+
+python:
+  - "3.7"
 
 services:
   - docker
diff --git a/README.md b/README.md
index b8699fa..76c0b70 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,6 @@
 ```
 wsk action update myAction myAction.py --docker openwhisk/python3action:1.0.2
 ```
-Replace `python3action` with `python2action` to use python 2.
-
 
 ### To use on deployment that contains the rutime as a kind
 To use as a kind action using python 3
@@ -52,7 +50,6 @@
 docker login
 ./gradlew core:pythonAction:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io
 ```
-Replace `core:pythonAction` with `core:python2Action` to build python 2 instead.
 
 Deploy OpenWhisk using ansible environment that contains the kind `python:3` and `python:2`
 Assuming you have OpenWhisk already deploy locally and `OPENWHISK_HOME` pointing to root directory of OpenWhisk core repository.
@@ -86,7 +83,6 @@
 wsk action update myAction myAction.py --docker $user_prefix/python3action
 ```
 The `$user_prefix` is usually your dockerhub user id.
-Replace `python3action` with `python2action` to use python 2
 
 ### Testing
 Install dependencies from the root directory on $OPENWHISK_HOME repository
diff --git a/core/python2Action/CHANGELOG.md b/core/python2Action/CHANGELOG.md
deleted file mode 100644
index d2ce6c5..0000000
--- a/core/python2Action/CHANGELOG.md
+++ /dev/null
@@ -1,78 +0,0 @@
-<!--
-#
-# 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.
-#
--->
-
-# Python 2 OpenWhisk Runtime Container
-
-## 1.0.3
-Changes:
-  - Update base image to openwhisk/dockerskeleton:1.3.3
-
-## 1.0.2
-Changes:
-  - Update base image to openwhisk/dockerskeleton:1.3.2
-
-## 1.0.1
-Changes:
-  - Update base image to openwhisk/dockerskeleton:1.3.1
-
-## 1.0.0
-Initial release.
-
-Python version = 2.7.12
-
-- asn1crypto (0.22.0)
-- attrs (17.2.0)
-- beautifulsoup4 (4.5.1)
-- cffi (1.10.0)
-- click (6.7)
-- cryptography (2.0.3)
-- cssselect (1.0.1)
-- enum34 (1.1.6)
-- Flask (0.11.1)
-- gevent (1.1.2)
-- greenlet (0.4.12)
-- httplib2 (0.9.2)
-- idna (2.6)
-- ipaddress (1.0.18)
-- itsdangerous (0.24)
-- Jinja2 (2.9.6)
-- kafka-python (1.3.1)
-- lxml (3.6.4)
-- MarkupSafe (1.0)
-- parsel (1.2.0)
-- pip (9.0.1)
-- pyasn1 (0.3.3)
-- pyasn1-modules (0.1.1)
-- pycparser (2.18)
-- PyDispatcher (2.0.5)
-- pyOpenSSL (17.2.0)
-- python-dateutil (2.5.3)
-- queuelib (1.4.2)
-- requests (2.11.1)
-- Scrapy (1.1.2)
-- service-identity (17.0.0)
-- setuptools (36.4.0)
-- simplejson (3.8.2)
-- six (1.10.0)
-- Twisted (16.4.0)
-- virtualenv (15.1.0)
-- w3lib (1.18.0)
-- Werkzeug (0.12.2)
-- wheel (0.29.0)
-- zope.interface (4.4.2)
diff --git a/core/python2Action/Dockerfile b/core/python2Action/Dockerfile
deleted file mode 100644
index 71daa5a..0000000
--- a/core/python2Action/Dockerfile
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# 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.
-#
-
-# Dockerfile for Python 2 actions, similar to the Python 3-based core/pythonAction
-FROM python:2.7-alpine
-
-# Upgrade and install basic Python dependencies
-RUN apk add --no-cache \
-        bash \
-        bzip2-dev \
-        gcc \
-        libc-dev \
-        libxslt-dev \
-        libxml2-dev \
-        libffi-dev \
-        linux-headers \
-        openssl-dev \
-        python-dev
-
-# Install common modules for python
-RUN pip install --no-cache-dir --upgrade pip setuptools six \
- && pip install --no-cache-dir \
-        gevent==1.3.6 \
-        flask==1.0.2 \
-        beautifulsoup4==4.6.3 \
-        httplib2==0.11.3 \
-        kafka_python==1.4.3 \
-        lxml==4.2.5 \
-        python-dateutil==2.7.3 \
-        requests==2.19.1 \
-        scrapy==1.5.1 \
-        simplejson==3.16.0 \
-        virtualenv==16.0.0 \
-        twisted==18.7.0
-
-ENV FLASK_PROXY_PORT 8080
-
-# Add the action proxy
-ADD https://raw.githubusercontent.com/apache/openwhisk-runtime-docker/master/core/actionProxy/actionproxy.py /actionProxy/actionproxy.py
-
-ADD pythonrunner.py /pythonAction/
-RUN mkdir -p /action
-
-CMD ["/bin/bash", "-c", "cd pythonAction && python -u pythonrunner.py"]
diff --git a/core/pythonActionLoop/Dockerfile b/core/pythonActionLoop/Dockerfile
index da2d46a..1144297 100644
--- a/core/pythonActionLoop/Dockerfile
+++ b/core/pythonActionLoop/Dockerfile
@@ -14,14 +14,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-FROM golang:1.11 as builder
-ENV PROXY_SOURCE=https://github.com/apache/openwhisk-runtime-go/archive/golang1.11@1.13.0-incubating.tar.gz
-RUN curl -L "$PROXY_SOURCE" | tar xzf - \
-  && mkdir -p src/github.com/apache \
-  && mv openwhisk-runtime-go-golang1.11-1.13.0-incubating \
-     src/github.com/apache/incubator-openwhisk-runtime-go \
-  && cd src/github.com/apache/incubator-openwhisk-runtime-go/main \
-  && CGO_ENABLED=0 go build -o /bin/proxy
+FROM golang:1.12 as builder
+RUN env CGO_ENABLED=0 go get github.com/apache/openwhisk-runtime-go/main && mv /go/bin/main /bin/proxy
+#ENV PROXY_SOURCE=https://github.com/apache/openwhisk-runtime-go/archive/golang1.11@1.13.0-incubating.tar.gz
+#RUN curl -L "$PROXY_SOURCE" | tar xzf - \
+#  && mkdir -p src/github.com/apache \
+#  && mv openwhisk-runtime-go-golang1.11-1.13.0-incubating \
+#     src/github.com/apache/incubator-openwhisk-runtime-go \
+#  && cd src/github.com/apache/incubator-openwhisk-runtime-go/main \
+#  && CGO_ENABLED=0 go build -o /bin/proxy
 
 FROM python:3.7-stretch
 
diff --git a/tests/build.gradle b/tests/build.gradle
index eb3a41c..e720728 100644
--- a/tests/build.gradle
+++ b/tests/build.gradle
@@ -44,5 +44,15 @@
 }
 
 task testPython3(type: Test) {
-    exclude 'runtime/actionContainers/Python2**'
 }
+
+task buildArtifacts(type:Exec) {
+    workingDir 'src/test/resources'
+    commandLine './build.sh'
+}
+
+tasks.withType(Test) {
+    dependsOn buildArtifacts
+}
+
+testClasses.dependsOn(buildArtifacts)
diff --git a/tests/src/test/resources/build.sh b/tests/src/test/resources/build.sh
new file mode 100755
index 0000000..e65166f
--- /dev/null
+++ b/tests/src/test/resources/build.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# 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.
+#
+
+set -e
+
+if [ -f ".built" ]; then
+  echo "Test zip artifacts already built, skipping"
+  exit 0
+fi
+
+# see what version of python is running
+py=$(python --version 2>&1 | awk -F' ' '{print $2}')
+if [[ $py == 3.7.* ]]; then
+  echo "python version is $py (ok)"
+else
+  echo "python version is $py (not ok)"
+  echo "cannot generated test artifacts and tests will fail"
+  exit -1
+fi
+
+(cd python_virtualenv && ./build.sh && zip ../python_virtualenv.zip -r .)
+(cd python_virtualenv_invalid_main && ./build.sh && zip ../python_virtualenv_invalid_main.zip -r .)
+(cd python_virtualenv_invalid_venv && zip ../python_virtualenv_invalid_venv.zip -r .)
+
+touch .built
diff --git a/core/python2Action/build.gradle b/tests/src/test/resources/python_virtualenv/__main__.py
old mode 100644
new mode 100755
similarity index 67%
copy from core/python2Action/build.gradle
copy to tests/src/test/resources/python_virtualenv/__main__.py
index 32ab1b5..b0f4087
--- a/core/python2Action/build.gradle
+++ b/tests/src/test/resources/python_virtualenv/__main__.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+"""Python Hello virtualenv test.
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -14,21 +17,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+"""
 
-ext.dockerImageName = 'python2action'
-apply from: '../../gradle/docker.gradle'
-distDocker.dependsOn 'copyFiles'
-distDocker.finalizedBy 'rmFiles'
+from random_useragent.random_useragent import Randomize
 
-def runners = files(
-    new File(project(':core:pythonAction').projectDir, 'pythonrunner.py')
-)
+def main(args):
+    return {"agent": Randomize().random_agent('desktop','linux')}
 
-task copyFiles(type: Copy) {
-    from runners
-    into '.'
-}
-
-task rmFiles(type: Delete) {
-    delete runners.collect { it.getName() }
-}
+def naim(args):
+    return main(args)
diff --git a/tests/src/test/resources/python_virtualenv/build.sh b/tests/src/test/resources/python_virtualenv/build.sh
new file mode 100755
index 0000000..dbf5aa0
--- /dev/null
+++ b/tests/src/test/resources/python_virtualenv/build.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+#
+
+virtualenv virtualenv
+source virtualenv/bin/activate
+pip install -r requirements.txt
+deactivate
diff --git a/tests/src/test/resources/python_virtualenv/requirements.txt b/tests/src/test/resources/python_virtualenv/requirements.txt
new file mode 100644
index 0000000..9dcd8c5
--- /dev/null
+++ b/tests/src/test/resources/python_virtualenv/requirements.txt
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+random-useragent==1.0
diff --git a/tests/src/test/resources/python_virtualenv_invalid_main/build.sh b/tests/src/test/resources/python_virtualenv_invalid_main/build.sh
new file mode 100755
index 0000000..dbf5aa0
--- /dev/null
+++ b/tests/src/test/resources/python_virtualenv_invalid_main/build.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+#
+
+virtualenv virtualenv
+source virtualenv/bin/activate
+pip install -r requirements.txt
+deactivate
diff --git a/core/python2Action/build.gradle b/tests/src/test/resources/python_virtualenv_invalid_main/mymain.py
old mode 100644
new mode 100755
similarity index 67%
copy from core/python2Action/build.gradle
copy to tests/src/test/resources/python_virtualenv_invalid_main/mymain.py
index 32ab1b5..b0f4087
--- a/core/python2Action/build.gradle
+++ b/tests/src/test/resources/python_virtualenv_invalid_main/mymain.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+"""Python Hello virtualenv test.
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -14,21 +17,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+"""
 
-ext.dockerImageName = 'python2action'
-apply from: '../../gradle/docker.gradle'
-distDocker.dependsOn 'copyFiles'
-distDocker.finalizedBy 'rmFiles'
+from random_useragent.random_useragent import Randomize
 
-def runners = files(
-    new File(project(':core:pythonAction').projectDir, 'pythonrunner.py')
-)
+def main(args):
+    return {"agent": Randomize().random_agent('desktop','linux')}
 
-task copyFiles(type: Copy) {
-    from runners
-    into '.'
-}
-
-task rmFiles(type: Delete) {
-    delete runners.collect { it.getName() }
-}
+def naim(args):
+    return main(args)
diff --git a/tests/src/test/resources/python_virtualenv_invalid_main/requirements.txt b/tests/src/test/resources/python_virtualenv_invalid_main/requirements.txt
new file mode 100644
index 0000000..9dcd8c5
--- /dev/null
+++ b/tests/src/test/resources/python_virtualenv_invalid_main/requirements.txt
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+random-useragent==1.0
diff --git a/core/python2Action/build.gradle b/tests/src/test/resources/python_virtualenv_invalid_venv/__main__.py
old mode 100644
new mode 100755
similarity index 67%
rename from core/python2Action/build.gradle
rename to tests/src/test/resources/python_virtualenv_invalid_venv/__main__.py
index 32ab1b5..b7111f4
--- a/core/python2Action/build.gradle
+++ b/tests/src/test/resources/python_virtualenv_invalid_venv/__main__.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+"""Python Hello virtualenv test.
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -14,21 +17,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+"""
 
-ext.dockerImageName = 'python2action'
-apply from: '../../gradle/docker.gradle'
-distDocker.dependsOn 'copyFiles'
-distDocker.finalizedBy 'rmFiles'
+def main(args):
+    return args
 
-def runners = files(
-    new File(project(':core:pythonAction').projectDir, 'pythonrunner.py')
-)
-
-task copyFiles(type: Copy) {
-    from runners
-    into '.'
-}
-
-task rmFiles(type: Delete) {
-    delete runners.collect { it.getName() }
-}
+def naim(dict):
+    return main(dict)
diff --git a/tests/src/test/resources/python_virtualenv_invalid_venv/virtualenv/invalidvirtualenv.md b/tests/src/test/resources/python_virtualenv_invalid_venv/virtualenv/invalidvirtualenv.md
new file mode 100644
index 0000000..486debf
--- /dev/null
+++ b/tests/src/test/resources/python_virtualenv_invalid_venv/virtualenv/invalidvirtualenv.md
@@ -0,0 +1,23 @@
+<!--
+#
+# 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.
+#
+-->
+
+This is an empty virtualenv directory.
+
+It is used to test the behavior, when the zip file of a python action contains a directory named 'virtualenv', but not containing a virtualenv.
+
diff --git a/tests/src/test/scala/runtime/actionContainers/Python2ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Python2ActionContainerTests.scala
deleted file mode 100644
index 312361d..0000000
--- a/tests/src/test/scala/runtime/actionContainers/Python2ActionContainerTests.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-import common.WskActorSystem
-
-@RunWith(classOf[JUnitRunner])
-class Python2ActionContainerTests extends PythonActionContainerTests with WskActorSystem {
-
-  override lazy val imageName = "python2action"
-
-  /** indicates if strings in python are unicode by default (i.e., python3 -> true, python2.7 -> false) */
-  override lazy val pythonStringAsUnicode = false
-}
diff --git a/tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala
index 8adbabc..b74c974 100644
--- a/tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala
@@ -24,18 +24,13 @@
 import common.WskActorSystem
 import actionContainers.{ActionContainer, BasicActionRunnerTests}
 import actionContainers.ActionContainer.withContainer
-import actionContainers.ResourceHelpers.{readAsBase64, ZipBuilder}
-import common.TestUtils
-import java.nio.file.Paths
+import actionContainers.ResourceHelpers.ZipBuilder
 
 @RunWith(classOf[JUnitRunner])
 class PythonActionContainerTests extends BasicActionRunnerTests with WskActorSystem {
 
   lazy val imageName = "python3action"
 
-  /** indicates if strings in python are unicode by default (i.e., python3 -> true, python2.7 -> false) */
-  lazy val pythonStringAsUnicode = true
-
   /** indicates if errors are logged or returned in the answer */
   lazy val initErrorsAreLogged = true
 
@@ -79,23 +74,13 @@
       """.stripMargin)
 
   override val testUnicode =
-    TestConfig(if (pythonStringAsUnicode) {
-      """
+    TestConfig("""
         |def main(args):
         |    sep = args['delimiter']
         |    str = sep + " ☃ " + sep
         |    print(str)
         |    return {"winter" : str }
-      """.stripMargin.trim
-    } else {
-      """
-        |def main(args):
-        |    sep = args['delimiter']
-        |    str = sep + " ☃ ".decode('utf-8') + sep
-        |    print(str.encode('utf-8'))
-        |    return {"winter" : str }
-      """.stripMargin.trim
-    })
+      """.stripMargin.trim)
 
   override val testEnv =
     TestConfig("""
@@ -133,7 +118,6 @@
         """.stripMargin)
 
     val code = ZipBuilder.mkBase64Zip(srcs)
-    println(code)
 
     val (out, err) = withActionContainer() { c =>
       val (initCode, initRes) = c.init(initPayload(code, main = "niam"))
@@ -208,127 +192,6 @@
       })
   }
 
-  it should "run zipped Python action containing a virtual environment" in {
-    val zippedPythonAction =
-      if (imageName == "python2action") "python2_virtualenv.zip"
-      else if (imageName == "actionloop-python-v3.7") "python37_virtualenv.zip"
-      else "python3_virtualenv.zip"
-    val zippedPythonActionName = TestUtils.getTestActionFilename(zippedPythonAction)
-    val code = readAsBase64(Paths.get(zippedPythonActionName))
-
-    val (out, err) = withActionContainer() { c =>
-      val (initCode, initRes) = c.init(initPayload(code, main = "main"))
-      initCode should be(200)
-      val args = JsObject("msg" -> JsString("any"))
-      val (runCode, runRes) = c.run(runPayload(args))
-      runCode should be(200)
-      runRes.get.toString() should include("netmask")
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        o should include("netmask")
-        e shouldBe empty
-    })
-  }
-
-  it should "run zipped Python action containing a virtual environment with non-standard entry point" in {
-    val zippedPythonAction =
-      if (imageName == "python2action") "python2_virtualenv.zip"
-      else if (imageName == "actionloop-python-v3.7") "python37_virtualenv.zip"
-      else "python3_virtualenv.zip"
-    val zippedPythonActionName = TestUtils.getTestActionFilename(zippedPythonAction)
-
-    val code = readAsBase64(Paths.get(zippedPythonActionName))
-    val (out, err) = withActionContainer() { c =>
-      val (initCode, initRes) = c.init(initPayload(code, main = "naim"))
-      initCode should be(200)
-      val args = JsObject("msg" -> JsString("any"))
-      val (runCode, runRes) = c.run(runPayload(args))
-      runCode should be(200)
-      runRes.get.toString() should include("netmask")
-    }
-    checkStreams(out, err, {
-      case (o, e) =>
-        o should include("netmask")
-        e shouldBe empty
-    })
-
-  }
-
-  it should "report error if zipped Python action containing a virtual environment for wrong python version" in {
-    val zippedPythonAction = if (imageName == "python2action") "python3_virtualenv.zip" else "python2_virtualenv.zip"
-    val zippedPythonActionName = TestUtils.getTestActionFilename(zippedPythonAction)
-
-    val code = readAsBase64(Paths.get(zippedPythonActionName))
-
-    // temporary guard to comment out this test for python3aiaction
-    // until it is fixed (it does not detect the wrong virtual env)
-    if (imageName != "python3aiaction") {
-      val (out, err) = withActionContainer() { c =>
-        val (initCode, initRes) = c.init(initPayload(code, main = "main"))
-        if (initErrorsAreLogged) {
-          initCode should be(502)
-        } else {
-          // it actually means it is actionloop
-          // it checks the error at init time
-          initCode should be(502)
-          initRes.get.fields.get("error").get.toString() should include("No module")
-        }
-      }
-      if (initErrorsAreLogged)
-        checkStreams(
-          out,
-          err, {
-            case (o, e) =>
-              o shouldBe empty
-              if (imageName == "python2action") {
-                e should include("ImportError")
-              }
-              if (imageName == "python3action") {
-                e should include("ModuleNotFoundError")
-              }
-          })
-    }
-  }
-
-  it should "report error if zipped Python action has wrong main module name" in {
-    val zippedPythonActionWrongName = TestUtils.getTestActionFilename("python_virtualenv_name.zip")
-
-    val code = readAsBase64(Paths.get(zippedPythonActionWrongName))
-
-    val (out, err) = withActionContainer() { c =>
-      val (initCode, initRes) = c.init(initPayload(code, main = "main"))
-      initCode should be(502)
-      if (!initErrorsAreLogged)
-        initRes.get.fields.get("error").get.toString() should include("Zip file does not include mandatory files")
-    }
-    if (initErrorsAreLogged)
-      checkStreams(out, err, {
-        case (o, e) =>
-          o shouldBe empty
-          e should include("Zip file does not include __main__.py")
-      })
-  }
-
-  it should "report error if zipped Python action has invalid virtualenv directory" in {
-    val zippedPythonActionWrongDir = TestUtils.getTestActionFilename("python_virtualenv_dir.zip")
-
-    val code = readAsBase64(Paths.get(zippedPythonActionWrongDir))
-    val (out, err) = withActionContainer() { c =>
-      val (initCode, initRes) = c.init(initPayload(code, main = "main"))
-      initCode should be(502)
-      if (!initErrorsAreLogged)
-        initRes.get.fields.get("error").get.toString() should include("Invalid virtualenv. Zip file does not include")
-    }
-    if (initErrorsAreLogged)
-      checkStreams(out, err, {
-        case (o, e) =>
-          o shouldBe empty
-          e should include("Zip file does not include /virtualenv/bin/")
-      })
-  }
-
   it should "return on action error when action fails" in {
     val (out, err) = withActionContainer() { c =>
       val code =
diff --git a/tests/src/test/scala/runtime/actionContainers/PythonActionLoopContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/PythonActionLoopContainerTests.scala
index 56a2dce..9a5fca9 100644
--- a/tests/src/test/scala/runtime/actionContainers/PythonActionLoopContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/PythonActionLoopContainerTests.scala
@@ -17,9 +17,13 @@
 
 package runtime.actionContainers
 
+import java.io.File
+
+import actionContainers.ResourceHelpers.readAsBase64
 import common.WskActorSystem
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
+import spray.json._
 
 @RunWith(classOf[JUnitRunner])
 class PythonActionLoopContainerTests extends PythonActionContainerTests with WskActorSystem {
@@ -28,9 +32,78 @@
 
   override val testNoSource = TestConfig("", hasCodeStub = false)
 
-  /** indicates if strings in python are unicode by default (i.e., python3 -> true, python2.7 -> false) */
-  override lazy val pythonStringAsUnicode = true
-
   /** actionloop based image does not log init errors - return the error in the body */
   override lazy val initErrorsAreLogged = false
+
+  def testArtifact(name: String): File = {
+    new File(this.getClass.getClassLoader.getResource(name).toURI)
+  }
+
+  it should "run zipped Python action containing a virtual environment" in {
+    val zippedPythonAction = testArtifact("python_virtualenv.zip")
+    val code = readAsBase64(zippedPythonAction.toPath)
+
+    withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code))
+      initCode should be(200)
+
+      val (runCode, runRes) = c.run(runPayload(JsObject.empty))
+      runCode should be(200)
+      runRes.get.prettyPrint should include("\"agent\"")
+    }
+  }
+
+  it should "run zipped Python action containing a virtual environment with non-standard entry point" in {
+    val zippedPythonAction = testArtifact("python_virtualenv.zip")
+    val code = readAsBase64(zippedPythonAction.toPath)
+
+    withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code, main = "naim"))
+      initCode should be(200)
+
+      val (runCode, runRes) = c.run(runPayload(JsObject.empty))
+      runCode should be(200)
+      runRes.get.prettyPrint should include("\"agent\"")
+    }
+  }
+
+  it should "report error if zipped Python action has wrong main module name" in {
+    val zippedPythonAction = testArtifact("python_virtualenv_invalid_main.zip")
+    val code = readAsBase64(zippedPythonAction.toPath)
+
+    val (out, err) = withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code, main = "main"))
+      initCode should be(502)
+
+      if (!initErrorsAreLogged)
+        initRes.get.fields.get("error").get.toString should include("Zip file does not include mandatory files")
+    }
+
+    if (initErrorsAreLogged)
+      checkStreams(out, err, {
+        case (o, e) =>
+          o shouldBe empty
+          e should include("Zip file does not include __main__.py")
+      })
+  }
+
+  it should "report error if zipped Python action has invalid virtualenv directory" in {
+    val zippedPythonAction = testArtifact("python_virtualenv_invalid_venv.zip")
+    val code = readAsBase64(zippedPythonAction.toPath)
+
+    val (out, err) = withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code, main = "main"))
+      initCode should be(502)
+
+      if (!initErrorsAreLogged)
+        initRes.get.fields.get("error").get.toString should include("Invalid virtualenv. Zip file does not include")
+    }
+
+    if (initErrorsAreLogged)
+      checkStreams(out, err, {
+        case (o, e) =>
+          o shouldBe empty
+          e should include("Zip file does not include /virtualenv/bin/")
+      })
+  }
 }
diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh
index 064360c..add19e9 100755
--- a/tools/travis/publish.sh
+++ b/tools/travis/publish.sh
@@ -30,9 +30,7 @@
 RUNTIME_VERSION=$2
 IMAGE_TAG=$3
 
-if [ ${RUNTIME_VERSION} == "2" ]; then
-  RUNTIME="python2Action"
-elif [ ${RUNTIME_VERSION} == "3" ]; then
+if [ ${RUNTIME_VERSION} == "3" ]; then
   RUNTIME="pythonAction"
 elif [ ${RUNTIME_VERSION} == "3-ai" ]; then
   RUNTIME="python3AiAction"
diff --git a/tools/travis/setup.sh b/tools/travis/setup.sh
index 6315f1a..36dd210 100755
--- a/tools/travis/setup.sh
+++ b/tools/travis/setup.sh
@@ -24,6 +24,10 @@
 ROOTDIR="$SCRIPTDIR/../.."
 HOMEDIR="$SCRIPTDIR/../../../"
 
+# check python and pip versions
+python --version
+pip --version
+
 # clone OpenWhisk utilities repo. in order to run scanCode
 cd $HOMEDIR
 git clone https://github.com/apache/openwhisk-utilities.git