You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@openwhisk.apache.org by GitBox <gi...@apache.org> on 2017/12/02 01:15:07 UTC

[GitHub] dubeejw closed pull request #3018: remove swift3.0 support

dubeejw closed pull request #3018: remove swift3.0 support
URL: https://github.com/apache/incubator-openwhisk/pull/3018
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/actionRuntimes/actionProxy/README.md b/actionRuntimes/actionProxy/README.md
index e5e454d87f..ee5d5e91af 100644
--- a/actionRuntimes/actionProxy/README.md
+++ b/actionRuntimes/actionProxy/README.md
@@ -32,6 +32,6 @@ line of output to `stdout` is a valid JSON object serialized to string if the ac
 A return value is optional but must be a JSON object (properly serialized) if present.
 
 For an example implementation of an `ActionRunner` that overrides `epilogue()` and `build()` see the
-[Swift 3](../swift3Action/swift3runner.py) action proxy. An implementation of the runner for Python actions
-is available [here](../pythonAction/pythonrunner.py). Lastly, an example Docker action that uses `C` is
-available in this [example](../../sdk/docker/Dockerfile).
+[Swift 3.x](https://github.com/apache/incubator-openwhisk-runtime-swift/blob/master/core/swift3Action/swift3runner.py) action proxy. An implementation of the runner for Python actions
+is available [here](https://github.com/apache/incubator-openwhisk-runtime-python/blob/master/core/pythonAction/pythonrunner.py). Lastly, an example Docker action that uses `C` is
+available in this [example](https://github.com/apache/incubator-openwhisk-runtime-docker/blob/master/sdk/docker/Dockerfile).
diff --git a/actionRuntimes/swift3Action/Dockerfile b/actionRuntimes/swift3Action/Dockerfile
deleted file mode 100644
index d5cf339b7d..0000000000
--- a/actionRuntimes/swift3Action/Dockerfile
+++ /dev/null
@@ -1 +0,0 @@
-FROM openwhisk/swift3action:1.0.0
\ No newline at end of file
diff --git a/actionRuntimes/swift3Action/build.gradle b/actionRuntimes/swift3Action/build.gradle
deleted file mode 100644
index 5cdffa32fe..0000000000
--- a/actionRuntimes/swift3Action/build.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-ext.dockerImageName = 'swift3action'
-apply from: '../../gradle/docker.gradle'
-
diff --git a/ansible/group_vars/all b/ansible/group_vars/all
index 34b9d95bc1..0e89cc0757 100644
--- a/ansible/group_vars/all
+++ b/ansible/group_vars/all
@@ -73,7 +73,7 @@ runtimesManifestDefault:
     - kind: "swift:3"
       image:
         name: "swift3action"
-      deprecated: false
+      deprecated: true
     - kind: "swift:3.1.1"
       default: true
       image:
diff --git a/ansible/roles/couchdb/tasks/deploy.yml b/ansible/roles/couchdb/tasks/deploy.yml
index 88ec1eb50b..803cfe53bf 100644
--- a/ansible/roles/couchdb/tasks/deploy.yml
+++ b/ansible/roles/couchdb/tasks/deploy.yml
@@ -62,7 +62,7 @@
     user: "{{ db_username }}"
     password: "{{ db_password }}"
     force_basic_auth: yes
-  when: inventory_hostname == coordinator
+  when: (inventory_hostname == coordinator) and (db.instances|int >= 2)
 
 - name: add remote nodes to the cluster
   uri:
@@ -75,7 +75,7 @@
     user: "{{ db_username }}"
     password: "{{ db_password }}"
     force_basic_auth: yes
-  when: inventory_hostname != coordinator
+  when: (inventory_hostname != coordinator) and (db.instances|int >= 2)
 
 - name: finish the cluster setup mode
   uri:
@@ -88,7 +88,7 @@
     user: "{{ db_username }}"
     password: "{{ db_password }}"
     force_basic_auth: yes
-  when: inventory_hostname == coordinator
+  when: (inventory_hostname == coordinator) and (db.instances|int >= 2)
 
 - name: disable reduce limit on views
   uri:
diff --git a/core/controller/src/main/resources/apiv1swagger.json b/core/controller/src/main/resources/apiv1swagger.json
index 95f680c54c..c125e9487a 100644
--- a/core/controller/src/main/resources/apiv1swagger.json
+++ b/core/controller/src/main/resources/apiv1swagger.json
@@ -1558,7 +1558,6 @@
                         "nodejs:8",
                         "python:2",
                         "python:3",
-                        "swift:3",
                         "swift:3.1.1",
                         "java",
                         "blackbox"
diff --git a/docs/actions.md b/docs/actions.md
index d11daf9af5..41c521e6ff 100644
--- a/docs/actions.md
+++ b/docs/actions.md
@@ -810,9 +810,7 @@ follows:
 wsk action create helloSwift hello.swift
 ```
 
-The CLI automatically infers the type of the action from the source file extension. For `.swift` source files, the action runs using a Swift 3.1.1 runtime. You can also create an action that runs with Swift 3.0 by explicitly specifying the parameter `--kind swift:3`. See the Swift [reference](./reference.md#swift-actions) for more information about Swift 3.0 vs. 3.1.
-
-**Note:** The actions you created using the kind `swift:3` will continue to work for a short period, however you should begin migrating your deployment scripts and recompiling your swift actions using the new kind `swift:3.1.1`. Support for Swift 3.0 is deprecated and will be removed soon.
+The CLI automatically infers the type of the action from the source file extension. For `.swift` source files, the action runs using a Swift 3.1.1 runtime. See the Swift [reference](./reference.md#swift-actions) for more information about the Swift runtime.
 
 
 Action invocation is the same for Swift actions as it is for JavaScript actions:
diff --git a/docs/reference.md b/docs/reference.md
index 87e7c52395..643f8dee71 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -377,17 +377,9 @@ Python 2 actions are executed using Python 2.7.12. This is the default runtime f
 ## Swift actions
 
 ### Swift 3
-Swift 3 actions are executed using Swift 3.1.1  `--kind swift:3.1.1` or Swift 3.0.2 `--kind swift:3`, respectively.  
+Swift 3 actions are executed using Swift 3.1.1  `--kind swift:3.1.1`.  
 The default `--kind swift:default` is Swift 3.1.1.
 
-**Note:** The actions you created using the kind `swift:3` will continue to work for a short period, however you should begin migrating your deployment scripts and recompiling your swift actions using the new kind `swift:3.1.1`.
-
-
-Swift 3.0.2 actions can use the following packages:
-- KituraNet version 1.0.1, https://github.com/IBM-Swift/Kitura-net
-- SwiftyJSON version 14.2.0, https://github.com/IBM-Swift/SwiftyJSON
-- IBM Swift Watson SDK version 0.4.1, https://github.com/IBM-Swift/swift-watson-sdk
-
 Swift 3.1.1 actions can use the following packages:
 - KituraNet version 1.7.6, https://github.com/IBM-Swift/Kitura-net
 - SwiftyJSON version 15.0.1, https://github.com/IBM-Swift/SwiftyJSON
diff --git a/settings.gradle b/settings.gradle
index 441c6b660b..57c899386a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -7,7 +7,6 @@ include 'actionRuntimes:nodejs8Action'
 include 'actionRuntimes:actionProxy'
 include 'actionRuntimes:pythonAction'
 include 'actionRuntimes:python2Action'
-include 'actionRuntimes:swift3Action'
 include 'actionRuntimes:swift3.1.1Action'
 include 'actionRuntimes:javaAction'
 include 'actionRuntimes:php7.1Action'
diff --git a/tests/build.gradle b/tests/build.gradle
index 3492f1e95d..2aa602ac1f 100644
--- a/tests/build.gradle
+++ b/tests/build.gradle
@@ -44,7 +44,6 @@ test.dependsOn([
     ':actionRuntimes:pythonAction:distDocker',
     ':actionRuntimes:python2Action:distDocker',
     ':actionRuntimes:javaAction:distDocker',
-    ':actionRuntimes:swift3Action:distDocker',
     ':actionRuntimes:swift3.1.1Action:distDocker',
     ':sdk:docker:distDocker',
     ':tests:dat:blackbox:badaction:distDocker',
diff --git a/tests/dat/actions/helloSwift3.zip b/tests/dat/actions/helloSwift3.zip
deleted file mode 100644
index 356fcdb574..0000000000
Binary files a/tests/dat/actions/helloSwift3.zip and /dev/null differ
diff --git a/tests/src/test/scala/actionContainers/Swift311ActionContainerTests.scala b/tests/src/test/scala/actionContainers/Swift311ActionContainerTests.scala
index 337b40e8e4..24e0dc1ff7 100644
--- a/tests/src/test/scala/actionContainers/Swift311ActionContainerTests.scala
+++ b/tests/src/test/scala/actionContainers/Swift311ActionContainerTests.scala
@@ -17,14 +17,68 @@
 
 package actionContainers
 
+import java.io.File
+
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
+import ActionContainer.withContainer
+import common.WskActorSystem
+import spray.json.JsObject
+import spray.json.JsString
+import common.TestUtils
+
 @RunWith(classOf[JUnitRunner])
-class Swift311ActionContainerTests extends Swift3ActionContainerTests {
-  override lazy val swiftContainerImageName = "action-swift-v3.1.1"
+class Swift311ActionContainerTests extends BasicActionRunnerTests with WskActorSystem {
+
+  // note: "out" will likely not be empty in some swift build as the compiler
+  // prints status messages and there doesn't seem to be a way to quiet them
+  val enforceEmptyOutputStream = false
+  lazy val swiftContainerImageName = "action-swift-v3.1.1"
+  lazy val envCode = makeEnvCode("ProcessInfo.processInfo")
+
+  def makeEnvCode(processInfo: String) = ("""
+         |func main(args: [String: Any]) -> [String: Any] {
+         |     let env = """ + processInfo + """.environment
+         |     var a = "???"
+         |     var b = "???"
+         |     var c = "???"
+         |     var d = "???"
+         |     var e = "???"
+         |     var f = "???"
+         |     if let v : String = env["__OW_API_HOST"] {
+         |         a = "\(v)"
+         |     }
+         |     if let v : String = env["__OW_API_KEY"] {
+         |         b = "\(v)"
+         |     }
+         |     if let v : String = env["__OW_NAMESPACE"] {
+         |         c = "\(v)"
+         |     }
+         |     if let v : String = env["__OW_ACTION_NAME"] {
+         |         d = "\(v)"
+         |     }
+         |     if let v : String = env["__OW_ACTIVATION_ID"] {
+         |         e = "\(v)"
+         |     }
+         |     if let v : String = env["__OW_DEADLINE"] {
+         |         f = "\(v)"
+         |     }
+         |     return ["api_host": a, "api_key": b, "namespace": c, "action_name": d, "activation_id": e, "deadline": f]
+         |}
+         """).stripMargin
 
-  override lazy val watsonCode = """
+  lazy val errorCode = """
+                | // You need an indirection, or swiftc detects the div/0
+                | // at compile-time. Smart.
+                | func div(x: Int, y: Int) -> Int {
+                |     return x/y
+                | }
+                | func main(args: [String: Any]) -> [String: Any] {
+                |     return [ "divBy0": div(x:5, y:0) ]
+                | }
+            """.stripMargin
+  lazy val watsonCode = """
                 | import AlchemyDataNewsV1
                 | import ConversationV1
                 | import DiscoveryV1
@@ -41,5 +95,277 @@ class Swift311ActionContainerTests extends Swift3ActionContainerTests {
                 |     return ["message": "I compiled and was able to import Watson SDKs"]
                 | }
             """.stripMargin
-  override lazy val swiftBinaryName = "helloSwift311.zip"
+  lazy val swiftBinaryName = "helloSwift311.zip"
+
+  // Helpers specific to swift actions
+  override def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit) = {
+    withContainer(swiftContainerImageName, env)(code)
+  }
+
+  behavior of swiftContainerImageName
+
+  // remove this test: it will not even compile under Swift 3 anymore
+  // so it should not be possible to write an action that does not return
+  // a [String:Any]
+  /*testNotReturningJson(
+        """
+        |func main(args: [String: Any]) -> String {
+        |    return "not a json object"
+        |}
+        """.stripMargin)
+   */
+
+  testEcho(Seq {
+    (
+      "swift",
+      """
+         | import Foundation
+         |
+         | extension FileHandle : TextOutputStream {
+         |     public func write(_ string: String) {
+         |         guard let data = string.data(using: .utf8) else { return }
+         |         self.write(data)
+         |     }
+         | }
+         |
+         | func main(args: [String: Any]) -> [String: Any] {
+         |     print("hello stdout")
+         |     var standardError = FileHandle.standardError
+         |     print("hello stderr", to: &standardError)
+         |     return args
+         | }
+        """.stripMargin)
+  })
+
+  testUnicode(Seq {
+    (
+      "swift",
+      """
+         | func main(args: [String: Any]) -> [String: Any] {
+         |     if let str = args["delimiter"] as? String {
+         |         let msg = "\(str) ? \(str)"
+         |         print(msg)
+         |         return [ "winter" : msg ]
+         |     } else {
+         |         return [ "error" : "no delimiter" ]
+         |     }
+         | }
+         """.stripMargin.trim)
+  })
+
+  testEnv(Seq {
+    ("swift", envCode)
+  }, enforceEmptyOutputStream)
+
+  it should "support actions using non-default entry points" in {
+    withActionContainer() { c =>
+      val code = """
+                | func niam(args: [String: Any]) -> [String: Any] {
+                |     return [ "result": "it works" ]
+                | }
+                |""".stripMargin
+
+      val (initCode, initRes) = c.init(initPayload(code, main = "niam"))
+      initCode should be(200)
+
+      val (_, runRes) = c.run(runPayload(JsObject()))
+      runRes.get.fields.get("result") shouldBe Some(JsString("it works"))
+    }
+  }
+
+  it should "return some error on action error" in {
+    val (out, err) = withActionContainer() { c =>
+      val code = errorCode
+
+      val (initCode, _) = c.init(initPayload(code))
+      initCode should be(200)
+
+      val (runCode, runRes) = c.run(runPayload(JsObject()))
+      runCode should be(502)
+
+      runRes shouldBe defined
+      runRes.get.fields.get("error") shouldBe defined
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+
+  it should "log compilation errors" in {
+    val (out, err) = withActionContainer() { c =>
+      val code = """
+              | 10 PRINT "Hello!"
+              | 20 GOTO 10
+            """.stripMargin
+
+      val (initCode, _) = c.init(initPayload(code))
+      initCode should not be (200)
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e.toLowerCase should include("error")
+    })
+  }
+
+  it should "support application errors" in {
+    val (out, err) = withActionContainer() { c =>
+      val code = """
+                | func main(args: [String: Any]) -> [String: Any] {
+                |     return [ "error": "sorry" ]
+                | }
+            """.stripMargin
+
+      val (initCode, _) = c.init(initPayload(code))
+      initCode should be(200)
+
+      val (runCode, runRes) = c.run(runPayload(JsObject()))
+      runCode should be(200) // action writer returning an error is OK
+
+      runRes shouldBe defined
+      runRes should be(Some(JsObject("error" -> JsString("sorry"))))
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+
+  it should "support support multiple files in a zip file" in {
+    val zip = new File(TestUtils.getTestActionFilename("multiSwift.zip")).toPath
+    val code = ResourceHelpers.readAsBase64(zip)
+
+    val (out, err) = withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code))
+      initCode should be(200)
+
+      val args = JsObject()
+      val (runCode, runRes) = c.run(runPayload(args))
+
+      runCode should be(200)
+      runRes.get shouldBe JsObject("greeting" -> (JsString("Hello stranger!")))
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+
+  it should "support pre-compiled binary in a zip file" in {
+    val zip = new File(TestUtils.getTestActionFilename(swiftBinaryName)).toPath
+    val code = ResourceHelpers.readAsBase64(zip)
+
+    val (out, err) = withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code))
+      initCode should be(200)
+
+      val args = JsObject()
+      val (runCode, runRes) = c.run(runPayload(args))
+
+      runCode should be(200)
+      runRes.get shouldBe JsObject("greeting" -> (JsString("Hello stranger!")))
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+
+  it should "properly use KituraNet and Dispatch" in {
+    val (out, err) = withActionContainer() { c =>
+      val code = """
+                | import KituraNet
+                | import Foundation
+                | import Dispatch
+                | func main(args:[String: Any]) -> [String:Any] {
+                |       let retries = 3
+                |       var resp = [String:Any]()
+                |       var attempts = 0
+                |       if let url = args["getUrl"] as? String {
+                |           while attempts < retries {
+                |               let group = DispatchGroup()
+                |               let queue = DispatchQueue.global(qos: .default)
+                |               group.enter()
+                |               queue.async {
+                |                   HTTP.get(url, callback: { response in
+                |                       if let response = response {
+                |                           do {
+                |                               var jsonData = Data()
+                |                               try response.readAllData(into: &jsonData)
+                |                               if let dic = WhiskJsonUtils.jsonDataToDictionary(jsonData: jsonData) {
+                |                                   resp = dic
+                |                               } else {
+                |                                   resp = ["error":"response from server is not JSON"]
+                |                               }
+                |                           } catch {
+                |                              resp["error"] = error.localizedDescription
+                |                           }
+                |                       }
+                |                       group.leave()
+                |                   })
+                |               }
+                |            switch group.wait(timeout: DispatchTime.distantFuture) {
+                |                case DispatchTimeoutResult.success:
+                |                    resp["attempts"] = attempts
+                |                    return resp
+                |                case DispatchTimeoutResult.timedOut:
+                |                    attempts = attempts + 1
+                |            }
+                |        }
+                |     }
+                |     return ["status":"Exceeded \(retries) attempts, aborting."]
+                | }
+            """.stripMargin
+
+      val (initCode, _) = c.init(initPayload(code))
+
+      initCode should be(200)
+
+      val argss = List(JsObject("getUrl" -> JsString("https://openwhisk.ng.bluemix.net/api/v1")))
+
+      for (args <- argss) {
+        val (runCode, out) = c.run(runPayload(args))
+        runCode should be(200)
+      }
+    }
+
+    // in side try catch finally print (out file)
+    // in catch block an error has occurred, get docker logs and print
+    // throw
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+
+  it should "make Watson SDKs available to action authors" in {
+    val (out, err) = withActionContainer() { c =>
+      val code = watsonCode
+
+      val (initCode, _) = c.init(initPayload(code))
+
+      initCode should be(200)
+
+      val (runCode, out) = c.run(runPayload(JsObject()))
+      runCode should be(200)
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
 }
diff --git a/tests/src/test/scala/actionContainers/Swift3ActionContainerTests.scala b/tests/src/test/scala/actionContainers/Swift3ActionContainerTests.scala
deleted file mode 100644
index 28f1eb30cf..0000000000
--- a/tests/src/test/scala/actionContainers/Swift3ActionContainerTests.scala
+++ /dev/null
@@ -1,363 +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 actionContainers
-
-import java.io.File
-
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-
-import ActionContainer.withContainer
-import common.WskActorSystem
-import spray.json.JsObject
-import spray.json.JsString
-import common.TestUtils
-
-@RunWith(classOf[JUnitRunner])
-class Swift3ActionContainerTests extends BasicActionRunnerTests with WskActorSystem {
-
-  // note: "out" will likely not be empty in some swift build as the compiler
-  // prints status messages and there doesn't seem to be a way to quiet them
-  val enforceEmptyOutputStream = false
-  lazy val swiftContainerImageName = "swift3action"
-  lazy val envCode = makeEnvCode("ProcessInfo.processInfo")
-
-  def makeEnvCode(processInfo: String) = ("""
-         |func main(args: [String: Any]) -> [String: Any] {
-         |     let env = """ + processInfo + """.environment
-         |     var a = "???"
-         |     var b = "???"
-         |     var c = "???"
-         |     var d = "???"
-         |     var e = "???"
-         |     var f = "???"
-         |     if let v : String = env["__OW_API_HOST"] {
-         |         a = "\(v)"
-         |     }
-         |     if let v : String = env["__OW_API_KEY"] {
-         |         b = "\(v)"
-         |     }
-         |     if let v : String = env["__OW_NAMESPACE"] {
-         |         c = "\(v)"
-         |     }
-         |     if let v : String = env["__OW_ACTION_NAME"] {
-         |         d = "\(v)"
-         |     }
-         |     if let v : String = env["__OW_ACTIVATION_ID"] {
-         |         e = "\(v)"
-         |     }
-         |     if let v : String = env["__OW_DEADLINE"] {
-         |         f = "\(v)"
-         |     }
-         |     return ["api_host": a, "api_key": b, "namespace": c, "action_name": d, "activation_id": e, "deadline": f]
-         |}
-         """).stripMargin
-
-  lazy val errorCode = """
-                | // You need an indirection, or swiftc detects the div/0
-                | // at compile-time. Smart.
-                | func div(x: Int, y: Int) -> Int {
-                |     return x/y
-                | }
-                | func main(args: [String: Any]) -> [String: Any] {
-                |     return [ "divBy0": div(x:5, y:0) ]
-                | }
-            """.stripMargin
-  lazy val watsonCode = """
-                | import RestKit
-                | import WeatherCompanyData
-                | import AlchemyVision
-                |
-                | func main(args: [String:Any]) -> [String:Any] {
-                |     return ["message": "I compiled and was able to import Watson SDKs"]
-                | }
-            """.stripMargin
-  lazy val swiftBinaryName = "helloSwift3.zip"
-
-  // Helpers specific to swift actions
-  override def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit) = {
-    withContainer(swiftContainerImageName, env)(code)
-  }
-
-  behavior of swiftContainerImageName
-
-  // remove this test: it will not even compile under Swift 3 anymore
-  // so it should not be possible to write an action that does not return
-  // a [String:Any]
-  /*testNotReturningJson(
-        """
-        |func main(args: [String: Any]) -> String {
-        |    return "not a json object"
-        |}
-        """.stripMargin)
-   */
-
-  testEcho(Seq {
-    (
-      "swift",
-      """
-         | import Foundation
-         |
-         | extension FileHandle : TextOutputStream {
-         |     public func write(_ string: String) {
-         |         guard let data = string.data(using: .utf8) else { return }
-         |         self.write(data)
-         |     }
-         | }
-         |
-         | func main(args: [String: Any]) -> [String: Any] {
-         |     print("hello stdout")
-         |     var standardError = FileHandle.standardError
-         |     print("hello stderr", to: &standardError)
-         |     return args
-         | }
-        """.stripMargin)
-  })
-
-  testUnicode(Seq {
-    (
-      "swift",
-      """
-         | func main(args: [String: Any]) -> [String: Any] {
-         |     if let str = args["delimiter"] as? String {
-         |         let msg = "\(str) ? \(str)"
-         |         print(msg)
-         |         return [ "winter" : msg ]
-         |     } else {
-         |         return [ "error" : "no delimiter" ]
-         |     }
-         | }
-         """.stripMargin.trim)
-  })
-
-  testEnv(Seq {
-    ("swift", envCode)
-  }, enforceEmptyOutputStream)
-
-  it should "support actions using non-default entry points" in {
-    withActionContainer() { c =>
-      val code = """
-                | func niam(args: [String: Any]) -> [String: Any] {
-                |     return [ "result": "it works" ]
-                | }
-                |""".stripMargin
-
-      val (initCode, initRes) = c.init(initPayload(code, main = "niam"))
-      initCode should be(200)
-
-      val (_, runRes) = c.run(runPayload(JsObject()))
-      runRes.get.fields.get("result") shouldBe Some(JsString("it works"))
-    }
-  }
-
-  it should "return some error on action error" in {
-    val (out, err) = withActionContainer() { c =>
-      val code = errorCode
-
-      val (initCode, _) = c.init(initPayload(code))
-      initCode should be(200)
-
-      val (runCode, runRes) = c.run(runPayload(JsObject()))
-      runCode should be(502)
-
-      runRes shouldBe defined
-      runRes.get.fields.get("error") shouldBe defined
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e shouldBe empty
-    })
-  }
-
-  it should "log compilation errors" in {
-    val (out, err) = withActionContainer() { c =>
-      val code = """
-              | 10 PRINT "Hello!"
-              | 20 GOTO 10
-            """.stripMargin
-
-      val (initCode, _) = c.init(initPayload(code))
-      initCode should not be (200)
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e.toLowerCase should include("error")
-    })
-  }
-
-  it should "support application errors" in {
-    val (out, err) = withActionContainer() { c =>
-      val code = """
-                | func main(args: [String: Any]) -> [String: Any] {
-                |     return [ "error": "sorry" ]
-                | }
-            """.stripMargin
-
-      val (initCode, _) = c.init(initPayload(code))
-      initCode should be(200)
-
-      val (runCode, runRes) = c.run(runPayload(JsObject()))
-      runCode should be(200) // action writer returning an error is OK
-
-      runRes shouldBe defined
-      runRes should be(Some(JsObject("error" -> JsString("sorry"))))
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e shouldBe empty
-    })
-  }
-
-  it should "support support multiple files in a zip file" in {
-    val zip = new File(TestUtils.getTestActionFilename("multiSwift.zip")).toPath
-    val code = ResourceHelpers.readAsBase64(zip)
-
-    val (out, err) = withActionContainer() { c =>
-      val (initCode, initRes) = c.init(initPayload(code))
-      initCode should be(200)
-
-      val args = JsObject()
-      val (runCode, runRes) = c.run(runPayload(args))
-
-      runCode should be(200)
-      runRes.get shouldBe JsObject("greeting" -> (JsString("Hello stranger!")))
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e shouldBe empty
-    })
-  }
-
-  it should "support pre-compiled binary in a zip file" in {
-    val zip = new File(TestUtils.getTestActionFilename(swiftBinaryName)).toPath
-    val code = ResourceHelpers.readAsBase64(zip)
-
-    val (out, err) = withActionContainer() { c =>
-      val (initCode, initRes) = c.init(initPayload(code))
-      initCode should be(200)
-
-      val args = JsObject()
-      val (runCode, runRes) = c.run(runPayload(args))
-
-      runCode should be(200)
-      runRes.get shouldBe JsObject("greeting" -> (JsString("Hello stranger!")))
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e shouldBe empty
-    })
-  }
-
-  it should "properly use KituraNet and Dispatch" in {
-    val (out, err) = withActionContainer() { c =>
-      val code = """
-                | import KituraNet
-                | import Foundation
-                | import Dispatch
-                | func main(args:[String: Any]) -> [String:Any] {
-                |       let retries = 3
-                |       var resp = [String:Any]()
-                |       var attempts = 0
-                |       if let url = args["getUrl"] as? String {
-                |           while attempts < retries {
-                |               let group = DispatchGroup()
-                |               let queue = DispatchQueue.global(qos: .default)
-                |               group.enter()
-                |               queue.async {
-                |                   HTTP.get(url, callback: { response in
-                |                       if let response = response {
-                |                           do {
-                |                               var jsonData = Data()
-                |                               try response.readAllData(into: &jsonData)
-                |                               if let dic = WhiskJsonUtils.jsonDataToDictionary(jsonData: jsonData) {
-                |                                   resp = dic
-                |                               } else {
-                |                                   resp = ["error":"response from server is not JSON"]
-                |                               }
-                |                           } catch {
-                |                              resp["error"] = error.localizedDescription
-                |                           }
-                |                       }
-                |                       group.leave()
-                |                   })
-                |               }
-                |            switch group.wait(timeout: DispatchTime.distantFuture) {
-                |                case DispatchTimeoutResult.success:
-                |                    resp["attempts"] = attempts
-                |                    return resp
-                |                case DispatchTimeoutResult.timedOut:
-                |                    attempts = attempts + 1
-                |            }
-                |        }
-                |     }
-                |     return ["status":"Exceeded \(retries) attempts, aborting."]
-                | }
-            """.stripMargin
-
-      val (initCode, _) = c.init(initPayload(code))
-
-      initCode should be(200)
-
-      val argss = List(JsObject("getUrl" -> JsString("https://openwhisk.ng.bluemix.net/api/v1")))
-
-      for (args <- argss) {
-        val (runCode, out) = c.run(runPayload(args))
-        runCode should be(200)
-      }
-    }
-
-    // in side try catch finally print (out file)
-    // in catch block an error has occurred, get docker logs and print
-    // throw
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e shouldBe empty
-    })
-  }
-
-  it should "make Watson SDKs available to action authors" in {
-    val (out, err) = withActionContainer() { c =>
-      val code = watsonCode
-
-      val (initCode, _) = c.init(initPayload(code))
-
-      initCode should be(200)
-
-      val (runCode, out) = c.run(runPayload(JsObject()))
-      runCode should be(200)
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        if (enforceEmptyOutputStream) o shouldBe empty
-        e shouldBe empty
-    })
-  }
-}
diff --git a/tests/src/test/scala/system/basic/WskBasicSwift3Tests.scala b/tests/src/test/scala/system/basic/WskBasicSwift3Tests.scala
index db2317fb77..bd95619ea5 100644
--- a/tests/src/test/scala/system/basic/WskBasicSwift3Tests.scala
+++ b/tests/src/test/scala/system/basic/WskBasicSwift3Tests.scala
@@ -37,7 +37,7 @@ abstract class WskBasicSwift3Tests extends TestHelpers with WskTestHelpers with
   implicit val wskprops: common.WskProps = WskProps()
   val wsk: BaseWsk
   val defaultAction: Some[String] = Some(TestUtils.getTestActionFilename("hello.swift"))
-  lazy val currentSwiftDefaultKind = "swift:3"
+  lazy val currentSwiftDefaultKind = "swift:3.1.1"
 
   behavior of "Swift runtime"
 
diff --git a/tests/src/test/scala/system/basic/WskRestBasicSwift311Tests.scala b/tests/src/test/scala/system/basic/WskRestBasicSwift311Tests.scala
index 1d954f2d9c..16e46a9070 100644
--- a/tests/src/test/scala/system/basic/WskRestBasicSwift311Tests.scala
+++ b/tests/src/test/scala/system/basic/WskRestBasicSwift311Tests.scala
@@ -25,5 +25,4 @@ import org.scalatest.junit.JUnitRunner
 @RunWith(classOf[JUnitRunner])
 class WskRestBasicSwift311Tests extends WskBasicSwift3Tests {
   override val wsk: common.rest.WskRest = new WskRest
-  override lazy val currentSwiftDefaultKind = "swift:3.1.1"
 }
diff --git a/tests/src/test/scala/system/basic/WskRestBasicSwift3Tests.scala b/tests/src/test/scala/system/basic/WskRestBasicSwift3Tests.scala
deleted file mode 100644
index 37359ed28b..0000000000
--- a/tests/src/test/scala/system/basic/WskRestBasicSwift3Tests.scala
+++ /dev/null
@@ -1,28 +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 system.basic
-
-import common.rest.WskRest
-
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-
-@RunWith(classOf[JUnitRunner])
-class WskRestBasicSwift3Tests extends WskBasicSwift3Tests {
-  override val wsk: common.rest.WskRest = new WskRest
-}
diff --git a/tests/src/test/scala/system/basic/WskRestUnicodeSwift3Tests.scala b/tests/src/test/scala/system/basic/WskRestUnicodeSwift3Tests.scala
deleted file mode 100644
index 4d1d924906..0000000000
--- a/tests/src/test/scala/system/basic/WskRestUnicodeSwift3Tests.scala
+++ /dev/null
@@ -1,34 +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 system.basic
-
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-
-import common.JsHelpers
-import common.WskTestHelpers
-import common.rest.WskRest
-
-@RunWith(classOf[JUnitRunner])
-class WskRestUnicodeSwift3Tests extends WskUnicodeTests with WskTestHelpers with JsHelpers {
-
-  override val wsk: common.rest.WskRest = new WskRest
-  override lazy val actionKind = "swift:3"
-  override lazy val actionSource = "unicode.swift"
-
-}
diff --git a/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala b/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala
index a879475d28..a00b1ea6e4 100644
--- a/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala
@@ -17,11 +17,200 @@
 
 package whisk.core.cli.test
 
+import scala.concurrent.duration.DurationInt
+import scala.language.postfixOps
 import org.junit.runner.RunWith
+import org.scalatest.Matchers
 import org.scalatest.junit.JUnitRunner
+import common.TestHelpers
+import common.TestUtils
+import common.rest.WskRest
+import common.WskProps
+import common.WskTestHelpers
+import spray.json.DefaultJsonProtocol.StringJsonFormat
+import spray.json.pimpAny
 
 @RunWith(classOf[JUnitRunner])
-class Swift311Tests extends Swift3Tests {
+class Swift311Tests extends TestHelpers with WskTestHelpers with Matchers {
 
-  override lazy val runtimeContainer = "swift:3.1.1"
+  implicit val wskprops = WskProps()
+  val wsk = new WskRest
+  val expectedDuration = 45 seconds
+  val activationPollDuration = 60 seconds
+
+  lazy val runtimeContainer = "swift:3.1.1"
+
+  behavior of "Swift Actions"
+
+  /**
+   * Test the Swift "hello world" demo sequence
+   */
+  it should "invoke a swift action" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+    val name = "helloSwift"
+    assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+      action.create(name, Some(TestUtils.getTestActionFilename("hello.swift")))
+    }
+
+    val start = System.currentTimeMillis()
+    withActivation(wsk.activation, wsk.action.invoke(name), totalWait = activationPollDuration) {
+      _.response.result.get.toString should include("Hello stranger!")
+    }
+
+    withActivation(
+      wsk.activation,
+      wsk.action.invoke(name, Map("name" -> "Sir".toJson)),
+      totalWait = activationPollDuration) {
+      _.response.result.get.toString should include("Hello Sir!")
+    }
+
+    withClue("Test duration exceeds expectation (ms)") {
+      val duration = System.currentTimeMillis() - start
+      duration should be <= expectedDuration.toMillis
+    }
+  }
+
+  behavior of "Swift Whisk SDK tests"
+
+  it should "allow Swift actions to invoke other actions" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+    // use CLI to create action from dat/actions/invokeAction.swift
+    val file = TestUtils.getTestActionFilename("invoke.swift")
+    val actionName = "invokeAction"
+    assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+      action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
+    }
+
+    // invoke the action
+    val run = wsk.action.invoke(actionName)
+    withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
+      // should be successful
+      activation.response.success shouldBe true
+
+      // should have a field named "activationId" which is the date action's activationId
+      activation.response.result.get.fields("activationId").toString.length should be >= 32
+
+      // check for "date" field that comes from invoking the date action
+      //activation.response.result.get.fieldPathExists("response", "result", "date") should be(true)
+    }
+  }
+
+  it should "allow Swift actions to invoke other actions and not block" in withAssetCleaner(wskprops) {
+    (wp, assetHelper) =>
+      // use CLI to create action from dat/actions/invokeNonBlocking.swift
+      val file = TestUtils.getTestActionFilename("invokeNonBlocking.swift")
+      val actionName = "invokeNonBlockingAction"
+      assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+        action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
+      }
+
+      // invoke the action
+      val run = wsk.action.invoke(actionName)
+      withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
+        // should not have a "response"
+        whisk.utils.JsHelpers.fieldPathExists(activation.response.result.get, "response") shouldBe false
+
+        // should have a field named "activationId" which is the date action's activationId
+        activation.response.result.get.fields("activationId").toString.length should be >= 32
+      }
+  }
+
+  it should "allow Swift actions to trigger events" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+    // create a trigger
+    val triggerName = s"TestTrigger ${System.currentTimeMillis()}"
+    assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
+      trigger.create(triggerName)
+    }
+
+    // create an action that fires the trigger
+    val file = TestUtils.getTestActionFilename("trigger.swift")
+    val actionName = "ActionThatTriggers"
+    assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+      action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
+    }
+
+    // invoke the action
+    val run = wsk.action.invoke(actionName, Map("triggerName" -> triggerName.toJson))
+    withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
+      // should be successful
+      activation.response.success shouldBe true
+
+      // should have a field named "activationId" which is the date action's activationId
+      activation.response.result.get.fields("activationId").toString.length should be >= 32
+
+      // should result in an activation for triggerName
+      val triggerActivations = wsk.activation.pollFor(1, Some(triggerName), retries = 20)
+      withClue(s"trigger activations for $triggerName:") {
+        triggerActivations.length should be(1)
+      }
+    }
+  }
+
+  it should "allow Swift actions to create a trigger" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+    // create an action that creates the trigger
+    val file = TestUtils.getTestActionFilename("createTrigger.swift")
+    val actionName = "ActionThatTriggers"
+
+    // the name of the trigger to create
+    val triggerName = s"TestTrigger ${System.currentTimeMillis()}"
+
+    assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+      assetHelper.withCleaner(wsk.trigger, triggerName) { (_, _) =>
+        // using an asset cleaner on the created trigger name will clean it up at the conclusion of the test
+        action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
+      }
+    }
+
+    // invoke the action
+    val run = wsk.action.invoke(actionName, Map("triggerName" -> triggerName.toJson))
+    withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
+      // should be successful
+      activation.response.success shouldBe true
+
+      // should have a field named "name" which is the name of the trigger created
+      activation.response.result.get.fields("name") shouldBe triggerName.toJson
+    }
+  }
+
+  it should "allow Swift actions to create a rule" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+    val ruleTriggerName = s"TestTrigger ${System.currentTimeMillis()}"
+    val ruleActionName = s"TestAction ${System.currentTimeMillis()}"
+    val ruleName = s"TestRule ${System.currentTimeMillis()}"
+
+    // create a dummy action and trigger for the rule
+    assetHelper.withCleaner(wsk.action, ruleActionName) { (action, name) =>
+      val dummyFile = TestUtils.getTestActionFilename("hello.swift")
+      action.create(name, artifact = Some(dummyFile), kind = Some(runtimeContainer))
+    }
+
+    assetHelper.withCleaner(wsk.trigger, ruleTriggerName) { (trigger, name) =>
+      assetHelper.withCleaner(wsk.rule, ruleName) { (_, _) =>
+        // using an asset cleaner on the created trigger name will clean it up at the conclusion of the test
+        trigger.create(name)
+      }
+    }
+
+    // create an action that creates the rule
+    val createRuleFile = TestUtils.getTestActionFilename("createRule.swift")
+    assetHelper.withCleaner(wsk.action, "ActionThatCreatesRule") { (action, name) =>
+      action.create(name, artifact = Some(createRuleFile), kind = Some(runtimeContainer))
+    }
+
+    // invoke the create rule action
+    val runCreateRule = wsk.action.invoke(
+      "ActionThatCreatesRule",
+      Map(
+        "triggerName" -> s"/_/$ruleTriggerName".toJson,
+        "actionName" -> s"/_/$ruleActionName".toJson,
+        "ruleName" -> ruleName.toJson))
+
+    withActivation(wsk.activation, runCreateRule, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
+      // should be successful
+      activation.response.success shouldBe true
+
+      // should have a field named "trigger" which is the name of the trigger associated with the rule
+      activation.response.result.get.fields("trigger").asJsObject.fields("name") shouldBe ruleTriggerName.toJson
+
+      // should have a field named "action" which is the name of the action associated with the rule
+      activation.response.result.get.fields("action").asJsObject.fields("name") shouldBe ruleActionName.toJson
+    }
+  }
 }
diff --git a/tests/src/test/scala/whisk/core/cli/test/Swift3Tests.scala b/tests/src/test/scala/whisk/core/cli/test/Swift3Tests.scala
deleted file mode 100644
index 5d4c02fa6c..0000000000
--- a/tests/src/test/scala/whisk/core/cli/test/Swift3Tests.scala
+++ /dev/null
@@ -1,216 +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 whisk.core.cli.test
-
-import scala.concurrent.duration.DurationInt
-import scala.language.postfixOps
-import org.junit.runner.RunWith
-import org.scalatest.Matchers
-import org.scalatest.junit.JUnitRunner
-import common.TestHelpers
-import common.TestUtils
-import common.rest.WskRest
-import common.WskProps
-import common.WskTestHelpers
-import spray.json.DefaultJsonProtocol.StringJsonFormat
-import spray.json.pimpAny
-
-@RunWith(classOf[JUnitRunner])
-class Swift3Tests extends TestHelpers with WskTestHelpers with Matchers {
-
-  implicit val wskprops = WskProps()
-  val wsk = new WskRest
-  val expectedDuration = 45 seconds
-  val activationPollDuration = 60 seconds
-
-  lazy val runtimeContainer = "swift:3"
-
-  behavior of "Swift Actions"
-
-  /**
-   * Test the Swift "hello world" demo sequence
-   */
-  it should "invoke a swift 3 action" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
-    val name = "helloSwift"
-    assetHelper.withCleaner(wsk.action, name) { (action, _) =>
-      action.create(name, Some(TestUtils.getTestActionFilename("hello.swift")))
-    }
-
-    val start = System.currentTimeMillis()
-    withActivation(wsk.activation, wsk.action.invoke(name), totalWait = activationPollDuration) {
-      _.response.result.get.toString should include("Hello stranger!")
-    }
-
-    withActivation(
-      wsk.activation,
-      wsk.action.invoke(name, Map("name" -> "Sir".toJson)),
-      totalWait = activationPollDuration) {
-      _.response.result.get.toString should include("Hello Sir!")
-    }
-
-    withClue("Test duration exceeds expectation (ms)") {
-      val duration = System.currentTimeMillis() - start
-      duration should be <= expectedDuration.toMillis
-    }
-  }
-
-  behavior of "Swift 3 Whisk SDK tests"
-
-  it should "allow Swift actions to invoke other actions" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
-    // use CLI to create action from dat/actions/invokeAction.swift
-    val file = TestUtils.getTestActionFilename("invoke.swift")
-    val actionName = "invokeAction"
-    assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
-      action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
-    }
-
-    // invoke the action
-    val run = wsk.action.invoke(actionName)
-    withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
-      // should be successful
-      activation.response.success shouldBe true
-
-      // should have a field named "activationId" which is the date action's activationId
-      activation.response.result.get.fields("activationId").toString.length should be >= 32
-
-      // check for "date" field that comes from invoking the date action
-      //activation.response.result.get.fieldPathExists("response", "result", "date") should be(true)
-    }
-  }
-
-  it should "allow Swift actions to invoke other actions and not block" in withAssetCleaner(wskprops) {
-    (wp, assetHelper) =>
-      // use CLI to create action from dat/actions/invokeNonBlocking.swift
-      val file = TestUtils.getTestActionFilename("invokeNonBlocking.swift")
-      val actionName = "invokeNonBlockingAction"
-      assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
-        action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
-      }
-
-      // invoke the action
-      val run = wsk.action.invoke(actionName)
-      withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
-        // should not have a "response"
-        whisk.utils.JsHelpers.fieldPathExists(activation.response.result.get, "response") shouldBe false
-
-        // should have a field named "activationId" which is the date action's activationId
-        activation.response.result.get.fields("activationId").toString.length should be >= 32
-      }
-  }
-
-  it should "allow Swift actions to trigger events" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
-    // create a trigger
-    val triggerName = s"TestTrigger ${System.currentTimeMillis()}"
-    assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
-      trigger.create(triggerName)
-    }
-
-    // create an action that fires the trigger
-    val file = TestUtils.getTestActionFilename("trigger.swift")
-    val actionName = "ActionThatTriggers"
-    assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
-      action.create(name = actionName, artifact = Some(file), kind = Some(runtimeContainer))
-    }
-
-    // invoke the action
-    val run = wsk.action.invoke(actionName, Map("triggerName" -> triggerName.toJson))
-    withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
-      // should be successful
-      activation.response.success shouldBe true
-
-      // should have a field named "activationId" which is the date action's activationId
-      activation.response.result.get.fields("activationId").toString.length should be >= 32
-
-      // should result in an activation for triggerName
-      val triggerActivations = wsk.activation.pollFor(1, Some(triggerName), retries = 20)
-      withClue(s"trigger activations for $triggerName:") {
-        triggerActivations.length should be(1)
-      }
-    }
-  }
-
-  it should "allow Swift actions to create a trigger" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
-    // create an action that creates the trigger
-    val file = TestUtils.getTestActionFilename("createTrigger.swift")
-    val actionName = "ActionThatTriggers"
-
-    // the name of the trigger to create
-    val triggerName = s"TestTrigger ${System.currentTimeMillis()}"
-
-    assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
-      assetHelper.withCleaner(wsk.trigger, triggerName) { (_, _) =>
-        // using an asset cleaner on the created trigger name will clean it up at the conclusion of the test
-        action.create(name = actionName, artifact = Some(file), kind = Some("swift:3"))
-      }
-    }
-
-    // invoke the action
-    val run = wsk.action.invoke(actionName, Map("triggerName" -> triggerName.toJson))
-    withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
-      // should be successful
-      activation.response.success shouldBe true
-
-      // should have a field named "name" which is the name of the trigger created
-      activation.response.result.get.fields("name") shouldBe triggerName.toJson
-    }
-  }
-
-  it should "allow Swift actions to create a rule" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
-    val ruleTriggerName = s"TestTrigger ${System.currentTimeMillis()}"
-    val ruleActionName = s"TestAction ${System.currentTimeMillis()}"
-    val ruleName = s"TestRule ${System.currentTimeMillis()}"
-
-    // create a dummy action and trigger for the rule
-    assetHelper.withCleaner(wsk.action, ruleActionName) { (action, name) =>
-      val dummyFile = TestUtils.getTestActionFilename("hello.swift")
-      action.create(name, artifact = Some(dummyFile), kind = Some("swift:3"))
-    }
-
-    assetHelper.withCleaner(wsk.trigger, ruleTriggerName) { (trigger, name) =>
-      assetHelper.withCleaner(wsk.rule, ruleName) { (_, _) =>
-        // using an asset cleaner on the created trigger name will clean it up at the conclusion of the test
-        trigger.create(name)
-      }
-    }
-
-    // create an action that creates the rule
-    val createRuleFile = TestUtils.getTestActionFilename("createRule.swift")
-    assetHelper.withCleaner(wsk.action, "ActionThatCreatesRule") { (action, name) =>
-      action.create(name, artifact = Some(createRuleFile), kind = Some("swift:3"))
-    }
-
-    // invoke the create rule action
-    val runCreateRule = wsk.action.invoke(
-      "ActionThatCreatesRule",
-      Map(
-        "triggerName" -> s"/_/$ruleTriggerName".toJson,
-        "actionName" -> s"/_/$ruleActionName".toJson,
-        "ruleName" -> ruleName.toJson))
-
-    withActivation(wsk.activation, runCreateRule, initialWait = 5 seconds, totalWait = 60 seconds) { activation =>
-      // should be successful
-      activation.response.success shouldBe true
-
-      // should have a field named "trigger" which is the name of the trigger associated with the rule
-      activation.response.result.get.fields("trigger").asJsObject.fields("name") shouldBe ruleTriggerName.toJson
-
-      // should have a field named "action" which is the name of the action associated with the rule
-      activation.response.result.get.fields("action").asJsObject.fields("name") shouldBe ruleActionName.toJson
-    }
-  }
-}
diff --git a/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala b/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
index 3d0be03e20..79eb18d949 100644
--- a/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
+++ b/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
@@ -36,10 +36,16 @@ trait ExecHelpers extends Matchers with WskActorSystem with StreamLogging {
   protected val NODEJS = "nodejs"
   protected val NODEJS6 = "nodejs:6"
   protected val SWIFT = "swift"
-  protected val SWIFT3 = "swift:3"
-
-  protected def imagename(name: String) =
-    ExecManifest.ImageName(s"${name}action".replace(":", ""), Some("openwhisk"), Some("latest"))
+  protected val SWIFT3 = "swift:3.1.1"
+  protected val SWIFT3_IMAGE = "action-swift-v3.1.1"
+
+  protected def imagename(name: String) = {
+    var image = s"${name}action".replace(":", "")
+    if (name.equals(SWIFT3)) {
+      image = SWIFT3_IMAGE
+    }
+    ExecManifest.ImageName(image, Some("openwhisk"), Some("latest"))
+  }
 
   protected def js(code: String, main: Option[String] = None) = {
     CodeExecAsString(RuntimeManifest(NODEJS, imagename(NODEJS), deprecated = Some(true)), trim(code), main.map(_.trim))
diff --git a/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala b/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala
index 1fa654b865..dd3fa042a4 100644
--- a/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala
+++ b/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala
@@ -400,7 +400,7 @@ class SchemaTests extends FlatSpec with BeforeAndAfter with ExecHelpers with Mat
       JsObject("kind" -> "nodejs:6".toJson, "code" -> "js1".toJson, "binary" -> false.toJson),
       JsObject("kind" -> "nodejs:6".toJson, "code" -> "js2".toJson, "binary" -> false.toJson, "foo" -> "bar".toJson),
       JsObject("kind" -> "swift".toJson, "code" -> "swift1".toJson, "binary" -> false.toJson),
-      JsObject("kind" -> "swift:3".toJson, "code" -> b64Body.toJson, "binary" -> true.toJson),
+      JsObject("kind" -> "swift:3.1.1".toJson, "code" -> b64Body.toJson, "binary" -> true.toJson),
       JsObject("kind" -> "nodejs:6".toJson, "code" -> b64Body.toJson, "binary" -> true.toJson))
 
     val execs = json.map { e =>
diff --git a/tools/build/redo b/tools/build/redo
index 7f36b56b96..8b877b14aa 100755
--- a/tools/build/redo
+++ b/tools/build/redo
@@ -288,11 +288,6 @@ Components = [
                   yaml = False,
                   gradle = 'actionRuntimes:pythonAction'),
 
-    makeComponent('swift3action',
-                  'build swift v3 action container',
-                  yaml = False,
-                  gradle = 'actionRuntimes:swift3Action'),
-
     makeComponent('action-swift-v3.1.1',
                   'build swift v3.1.1 action container',
                   yaml = False,
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index d8e4d64f6c..043c1cc4d6 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -21,7 +21,7 @@ TERM=dumb ./gradlew checkScalafmtAll
 cd $ROOTDIR/ansible
 
 ANSIBLE_CMD="ansible-playbook -i environments/local -e docker_image_prefix=testing"
-GRADLE_PROJS_SKIP="-x :actionRuntimes:pythonAction:distDocker  -x :actionRuntimes:python2Action:distDocker -x :actionRuntimes:swift3Action:distDocker -x actionRuntimes:swift3.1.1Action:distDocker -x :actionRuntimes:javaAction:distDocker"
+GRADLE_PROJS_SKIP="-x :actionRuntimes:pythonAction:distDocker  -x :actionRuntimes:python2Action:distDocker -x actionRuntimes:swift3.1.1Action:distDocker -x :actionRuntimes:javaAction:distDocker"
 
 $ANSIBLE_CMD setup.yml
 $ANSIBLE_CMD prereq.yml


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services