浏览代码

Merge pull request #104 from kalliope-project/tests_nico

add some unit tests
Monf 8 年之前
父节点
当前提交
835be27821

+ 1 - 1
Docs/rest_api.md

@@ -134,7 +134,7 @@ Output example:
 ```
 
 
-Run a synapse from an order
+### Run a synapse from an order
 
 Normal response codes: 201
 Error response codes: unauthorized(401), itemNotFound(404)

+ 81 - 1
Tests/test_rest_api.py

@@ -136,7 +136,7 @@ class TestRestAPI(unittest.TestCase):
 
         self.assertEqual(expected_content, json.loads(result.content))
 
-    def test_synapse_not_found(self):
+    def test_get_synapse_not_found(self):
         url = self.base_url + "/synapses/test-none"
         result = requests.get(url=url)
 
@@ -147,6 +147,86 @@ class TestRestAPI(unittest.TestCase):
         }
 
         self.assertEqual(expected_content, json.loads(result.content))
+        self.assertEqual(result.status_code, 404)
+
+    def test_run_synapse_by_name(self):
+        url = self.base_url + "/synapses/test"
+        result = requests.post(url=url)
+
+        expected_content = {
+            "synapses": {
+                "name": "test",
+                "neurons": [
+                    {
+                        "say": {
+                            "message": [
+                                "test message"
+                            ]
+                        }
+                    }
+                ],
+                "signals": [
+                    {
+                        "order": "test_order"
+                    }
+                ]
+            }
+        }
+
+        self.assertEqual(expected_content, json.loads(result.content))
+        self.assertEqual(result.status_code, 201)
+
+    def test_post_synapse_not_found(self):
+        url = self.base_url + "/synapses/test-none"
+        result = requests.post(url=url)
+
+        expected_content = {
+            "error": {
+                "synapse name not found": "test-none"
+            }
+        }
+
+        self.assertEqual(expected_content, json.loads(result.content))
+        self.assertEqual(result.status_code, 404)
+
+    def test_run_synapse_with_order(self):
+        url = self.base_url + "/order/"
+        headers = {"Content-Type": "application/json"}
+        data = {"order": "test_order"}
+        result = requests.post(url=url, headers=headers, json=data)
+
+        expected_content = {
+            "synapses": [
+                {
+                    "name": "test",
+                    "neurons": [
+                        {
+                            "name": "say",
+                            "parameters": "{'message': ['test message']}"
+                        }
+                    ],
+                    "signals": [
+                        {
+                            "order": "test_order"
+                        }
+                    ]
+                }
+            ]
+        }
+
+        self.assertEqual(expected_content, json.loads(result.content))
+        self.assertEqual(result.status_code, 201)
+
+    def test_post_synapse_by_order_not_found(self):
+        url = self.base_url + "/order/"
+        data = {"order": "non existing order"}
+        headers = {"Content-Type": "application/json"}
+        result = requests.post(url=url, headers=headers, json=data)
+
+        expected_content = {'error': {'error': "The given order doesn't match any synapses"}}
+
+        self.assertEqual(expected_content, json.loads(result.content))
+        self.assertEqual(result.status_code, 400)
 
 if __name__ == '__main__':
     unittest.main()

+ 37 - 5
neurons/script/README.md

@@ -6,18 +6,23 @@ This neuron runs a script located on the Kalliope system.
 
 ## Options
 
-| parameter | required | default | choices | comment                             |
-|-----------|----------|---------|---------|-------------------------------------|
-| path      | YES      |         |         | The path of the script to execute.  |
+| parameter | required | default | choices | comment                                                                    |
+|-----------|----------|---------|---------|----------------------------------------------------------------------------|
+| path      | YES      |         |         | The path of the script to execute.                                         |
+| async     | NO       | FALSE   |         | If True, Kalliope will not wait for the end of the execution of the script |
 
 ## Return Values
 
-No returned values
+Values are only returned by the neuron if the async mode is set to `False`.
+
+| Name       | Description                                                                                           | Type   | sample                        |
+|------------|-------------------------------------------------------------------------------------------------------|--------|-------------------------------|
+| output     | The shell output of the command if any. The command "date" will retun "Sun Oct 16 15:50:45 CEST 2016" | string | Sun Oct 16 15:50:45 CEST 2016 |
+| returncode | The returned code of the command. Return 0 if the command was succesfuly exectued, else 1             | int    | 0                             |
 
 ## Synapses example
 
 Simple example : 
-
 ```
   - name: "run-simple-script"
     signals:
@@ -27,8 +32,35 @@ Simple example :
           path: "/path/to/script.sh"    
 ```
 
+If the script can take a long time and you don't want to block the Kalliope process, you can run it in asynchronous mode.
+Keep in mind that you cannot get any returned value with this mode.
+
+```
+  - name: "run-simple-script"
+    signals:
+      - order: "Run the script"
+    neurons:
+      - script:
+          path: "/path/to/script.sh"   
+          async: True
+```
+
+Make Kalliope speak out loud the result of the script.
+```
+  - name: "run-script-an-give-output"
+    signals:
+      - order: "run the script"
+    neurons:
+      - script:
+          path: "/path/to/script.sh"   
+          say_template: "{{ output }}"
+```
+
 
 ## Notes
 
 > **Note:** Kalliope must have the rights to run the script.
+
 > **Note:** Kalliope can be used to grant access to an user with lower rights ... !
+
+> **Note:** When 'async' flag is used, returned value are lost

+ 39 - 2
neurons/script/script.py

@@ -1,18 +1,55 @@
 import subprocess
 import os
+import threading
 
 from core.NeuronModule import NeuronModule, MissingParameterException, InvalidParameterException
 
 
+class AsyncShell(threading.Thread):
+    """
+    Class used to run an asynchronous Shell command
+
+    .. notes:: Impossible to get the success code of the command
+    """
+    def __init__(self, path):
+        self.stdout = None
+        self.stderr = None
+        self.path = path
+        threading.Thread.__init__(self)
+
+    def run(self):
+        p = subprocess.Popen(self.path,
+                             shell=False,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+
+        self.stdout, self.stderr = p.communicate()
+
+
 class Script(NeuronModule):
     def __init__(self, **kwargs):
         super(Script, self).__init__(**kwargs)
         self.path = kwargs.get("path", None)
+        # get if the user select a blocking command or not
+        self.async = kwargs.get('async', False)
 
         # check parameters
         if self._is_parameters_ok():
-            p = subprocess.Popen(self.path, stdout=subprocess.PIPE, shell=True)
-            (output, err) = p.communicate()
+            # run the command
+            if not self.async:
+                p = subprocess.Popen(self.path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
+                (output, err) = p.communicate()
+                self.output = output
+                self.returncode = p.returncode
+                message = {
+                    "output": self.output,
+                    "returncode": self.returncode
+                }
+                self.say(message)
+
+            else:
+                async_shell = AsyncShell(path=self.path)
+                async_shell.start()
 
     def _is_parameters_ok(self):
         """

+ 60 - 2
neurons/script/tests/test_script.py

@@ -1,6 +1,10 @@
 import unittest
 import os
 
+import time
+
+from core import OrderAnalyser
+from core.ConfigurationManager import BrainLoader
 from core.NeuronModule import MissingParameterException, InvalidParameterException
 from neurons.script.script import Script
 from core.FileManager import FileManager
@@ -11,6 +15,7 @@ class TestScript(unittest.TestCase):
     def setUp(self):
         self.path = "path"
         self.random = "random"
+        self.test_file = "/tmp/kalliope_text_shell.txt"
 
     def testParameters(self):
         def run_test_missing_param(parameters_to_test):
@@ -44,7 +49,9 @@ class TestScript(unittest.TestCase):
         tmp_file_path = tmp_path+"neuronScript"
         if not os.path.exists(tmp_path):
             os.makedirs(tmp_path)
-        FileManager.write_in_file(tmp_file_path, "[kalliope-test] TestScript - testParameters")
+        text_to_write = "[kalliope-test] TestScript - testParameters"
+        with open(tmp_file_path, 'w') as myFile:
+            myFile.write(text_to_write)
         os.chmod(tmp_file_path, 0600)
         # test the user does not have access
         self.path = tmp_file_path
@@ -54,8 +61,59 @@ class TestScript(unittest.TestCase):
         run_test_invalid_param(parameters)
         # Remove the tmp file
         os.chmod(tmp_file_path, 0700)
-        FileManager.remove_file(tmp_file_path)
+        os.remove(tmp_file_path)
+
+    def test_script_execution(self):
+        """
+        Test we can run a script
+        """
+        param = {
+            "path": "./test_script.sh"
+        }
+
+        Script(**param)
+        self.assertTrue(os.path.isfile(self.test_file))
+
+        # remove the tet file
+        os.remove(self.test_file)
+
+    def test_script_execution_async(self):
+        """
+        Test we can run a script asynchronously
+        """
+        param = {
+            "path": "./test_script.sh",
+            "async": True
+        }
+
+        Script(**param)
+        # let the time to the thread to do its job
+        time.sleep(0.5)
+        self.assertTrue(os.path.isfile(self.test_file))
+
+        # remove the test file
+        os.remove(self.test_file)
+
+    def test_script_content(self):
+        """
+        Test we can get a content from the launched script
+        """
+        text_to_write = 'kalliope'
+        # we write a content into a file
+        with open(self.test_file, 'w') as myFile:
+            myFile.write(text_to_write)
+
+        # get the output with the neuron
+        parameters = {
+            "path": "./test_script_cat.sh",
+        }
+
+        script = Script(**parameters)
+        self.assertEqual(script.output, text_to_write)
+        self.assertEqual(script.returncode, 0)
 
+        # remove the tet file
+        os.remove(self.test_file)
 
 if __name__ == '__main__':
     unittest.main()

+ 3 - 0
neurons/script/tests/test_script.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+touch /tmp/kalliope_text_shell.txt

+ 3 - 0
neurons/script/tests/test_script_cat.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+cat /tmp/kalliope_text_shell.txt

+ 4 - 2
neurons/shell/shell.py

@@ -46,9 +46,11 @@ class Shell(NeuronModule):
             if not self.async:
                 p = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
                 (output, err) = p.communicate()
+                self.output = output
+                self.returncode = p.returncode
                 message = {
-                    "output": output,
-                    "returncode": p.returncode
+                    "output": self.output,
+                    "returncode": self.returncode
                 }
                 self.say(message)
 

+ 56 - 2
neurons/shell/tests/test_shell.py

@@ -1,5 +1,8 @@
+import os
 import unittest
 
+import time
+
 from core.NeuronModule import MissingParameterException
 from neurons.shell.shell import Shell
 
@@ -7,8 +10,9 @@ from neurons.shell.shell import Shell
 class TestShell(unittest.TestCase):
 
     def setUp(self):
-        self.cmd="cmd"
-        self.random="random"
+        self.cmd = "cmd"
+        self.random = "random"
+        self.test_file = "/tmp/kalliope_text_shell.txt"
 
     def testParameters(self):
         def run_test(parameters_to_test):
@@ -25,6 +29,56 @@ class TestShell(unittest.TestCase):
         }
         run_test(parameters)
 
+    def test_shell_returned_code(self):
+        """
+        To test that the shell neuron works, we ask it to create a file
+        """
+        parameters = {
+            "cmd": "touch %s" % self.test_file
+        }
+
+        shell = Shell(**parameters)
+        self.assertTrue(os.path.isfile(self.test_file))
+        self.assertEqual(shell.returncode, 0)
+        # remove the test file
+        os.remove(self.test_file)
+
+    def test_shell_content(self):
+        """
+        Test we can get a content from the launched command
+        """
+        text_to_write = 'kalliope'
+        # we write a content into a file
+        with open(self.test_file, 'w') as myFile:
+            myFile.write(text_to_write)
+
+        # get the output with the neuron
+        parameters = {
+            "cmd": "cat %s" % self.test_file
+        }
+
+        shell = Shell(**parameters)
+        self.assertEqual(shell.output, text_to_write)
+        self.assertEqual(shell.returncode, 0)
+        # remove the test file
+        os.remove(self.test_file)
+
+    def test_async_shell(self):
+        """
+        Test that the neuron can run a shell command asynchronously
+        """
+        parameters = {
+            "cmd": "touch %s" % self.test_file,
+            "async": True
+        }
+
+        Shell(**parameters)
+        # let the time the the thread to perform the action
+        time.sleep(0.5)
+        self.assertTrue(os.path.isfile(self.test_file))
+        # remove the test file
+        os.remove(self.test_file)
+
 
 if __name__ == '__main__':
     unittest.main()