From 73fcb84a8e241aadb5e64cf5d6506e9836d11307 Mon Sep 17 00:00:00 2001
From: Philip ABBET <philip.abbet@idiap.ch>
Date: Thu, 30 Nov 2017 15:26:16 +0100
Subject: [PATCH] [unittests] Add tests for the new algorithm APIs

---
 beat/backend/python/algorithm.py              | 218 +++--
 beat/backend/python/data_loaders.py           |  13 +-
 beat/backend/python/loader.py                 |   7 +-
 .../prefix/algorithms/autonomous/add/1.json   |  30 +
 .../prefix/algorithms/autonomous/add/1.py     |  49 +
 .../prefix/algorithms/autonomous/add2/1.json  |  28 +
 .../prefix/algorithms/autonomous/add2/1.py    |  51 ++
 .../prefix/algorithms/autonomous/add3/1.json  |  31 +
 .../prefix/algorithms/autonomous/add3/1.py    |  46 +
 .../prefix/algorithms/autonomous/echo/1.json  |  21 +
 .../prefix/algorithms/autonomous/echo/1.py    |  42 +
 .../algorithms/autonomous/no_prepare/1.json   |  24 +
 .../algorithms/autonomous/no_prepare/1.py     |  31 +
 .../algorithms/autonomous/no_setup/1.json     |  24 +
 .../algorithms/autonomous/no_setup/1.py       |  35 +
 .../algorithms/autonomous/parametrized/1.json | 279 ++++++
 .../algorithms/autonomous/parametrized/1.py   |  66 ++
 .../autonomous/prepare_crash/1.json           |  24 +
 .../prepare_crash}/1.py                       |  12 +-
 .../algorithms/autonomous/prepare_fail/1.json |  24 +
 .../algorithms/autonomous/prepare_fail/1.py   |  34 +
 .../algorithms/autonomous/setup_crash/1.json  |  24 +
 .../algorithms/autonomous/setup_crash/1.py    |  35 +
 .../algorithms/autonomous/setup_fail/1.json   |  24 +
 .../algorithms/autonomous/setup_fail/1.py     |  34 +
 .../test/prefix/algorithms/legacy/add/1.json  |  27 +
 .../test/prefix/algorithms/legacy/add/1.py    |  41 +
 .../test/prefix/algorithms/legacy/add2/1.json |  25 +
 .../{two_streams__with_delay => add2}/1.py    |  15 +-
 .../test/prefix/algorithms/legacy/echo/1.json |  18 +
 .../{one_stream__no_delay => echo}/1.py       |   2 +-
 .../prefix/algorithms/legacy/no_setup/1.json  |  22 +
 .../{two_streams__no_delay => no_setup}/1.py  |   4 -
 .../legacy/one_stream__no_delay/1.json        |  18 -
 .../legacy/one_stream__with_delay/1.json      |  18 -
 .../legacy/two_streams__no_delay/1.json       |  21 -
 .../legacy/two_streams__with_delay/1.json     |  21 -
 .../prefix/algorithms/sequential/add/1.json   |  30 +
 .../prefix/algorithms/sequential/add/1.py     |  41 +
 .../prefix/algorithms/sequential/add2/1.json  |  28 +
 .../prefix/algorithms/sequential/add2/1.py    |  44 +
 .../prefix/algorithms/sequential/add3/1.json  |  31 +
 .../prefix/algorithms/sequential/add3/1.py    |  40 +
 .../prefix/algorithms/sequential/echo/1.json  |  21 +
 .../prefix/algorithms/sequential/echo/1.py    |  32 +
 .../algorithms/sequential/no_prepare/1.json   |  24 +
 .../algorithms/sequential/no_prepare/1.py     |  31 +
 .../algorithms/sequential/no_setup/1.json     |  24 +
 .../algorithms/sequential/no_setup/1.py       |  35 +
 .../algorithms/sequential/parametrized/1.json | 279 ++++++
 .../algorithms/sequential/parametrized/1.py   |  66 ++
 .../sequential/prepare_crash/1.json           |  24 +
 .../algorithms/sequential/prepare_crash/1.py  |  35 +
 .../algorithms/sequential/prepare_fail/1.json |  24 +
 .../algorithms/sequential/prepare_fail/1.py   |  34 +
 .../algorithms/sequential/setup_crash/1.json  |  24 +
 .../algorithms/sequential/setup_crash/1.py    |  35 +
 .../algorithms/sequential/setup_fail/1.json   |  24 +
 .../algorithms/sequential/setup_fail/1.py     |  34 +
 beat/backend/python/test/test_algorithm.py    | 848 +++++++++++++++++-
 beat/backend/python/test/test_data_loaders.py | 121 ++-
 beat/backend/python/test/test_inputs.py       |   2 +-
 .../python/test/test_synchronization.py       | 424 ---------
 63 files changed, 3171 insertions(+), 622 deletions(-)
 mode change 100644 => 100755 beat/backend/python/loader.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/add/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/add/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/add2/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/add2/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/add3/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/add3/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/echo/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/echo/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.json
 rename beat/backend/python/test/prefix/algorithms/{legacy/one_stream__with_delay => autonomous/prepare_crash}/1.py (89%)
 mode change 100644 => 100755
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/legacy/add/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/legacy/add/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/legacy/add2/1.json
 rename beat/backend/python/test/prefix/algorithms/legacy/{two_streams__with_delay => add2}/1.py (87%)
 mode change 100644 => 100755
 create mode 100644 beat/backend/python/test/prefix/algorithms/legacy/echo/1.json
 rename beat/backend/python/test/prefix/algorithms/legacy/{one_stream__no_delay => echo}/1.py (97%)
 mode change 100644 => 100755
 create mode 100644 beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.json
 rename beat/backend/python/test/prefix/algorithms/legacy/{two_streams__no_delay => no_setup}/1.py (92%)
 delete mode 100644 beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.json
 delete mode 100644 beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.json
 delete mode 100644 beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.json
 delete mode 100644 beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.json
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/add/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/add/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/add2/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/add2/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/add3/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/add3/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/echo/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/echo/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.py
 create mode 100644 beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.json
 create mode 100755 beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.py
 delete mode 100644 beat/backend/python/test/test_synchronization.py

diff --git a/beat/backend/python/algorithm.py b/beat/backend/python/algorithm.py
index 607738d..ae577b2 100755
--- a/beat/backend/python/algorithm.py
+++ b/beat/backend/python/algorithm.py
@@ -90,15 +90,10 @@ class Runner(object):
         exception from the user code. Read the documention of :py:func:`run`
         for more details.
 
-      *args: Constructor parameters for the algorithm (normally none)
-
-      **kwargs: Constructor parameters for the algorithm (normally none)
-
     '''
 
 
-    def __init__(self, module, obj_name, algorithm, exc=None, *args,
-            **kwargs):
+    def __init__(self, module, obj_name, algorithm, exc=None):
 
         try:
             class_ = getattr(module, obj_name)
@@ -107,15 +102,15 @@ class Runner(object):
                 type, value, traceback = sys.exc_info()
                 six.reraise(exc, exc(value), traceback)
             else:
-                raise #just re-raise the user exception
+                raise # Just re-raise the user exception
 
-        self.obj = loader.run(class_, '__new__', exc, *args, **kwargs)
+        self.obj = loader.run(class_, '__new__', exc)
         self.name = module.__name__
         self.algorithm = algorithm
         self.exc = exc
 
-        # if the algorithm does not have a 'setup' method, it is ready by default
         self.ready = not hasattr(self.obj, 'setup')
+        self.prepared = (self.algorithm.api_version == 1) or not hasattr(self.obj, 'prepare')
 
 
     def _check_parameters(self, parameters):
@@ -125,7 +120,7 @@ class Runner(object):
         algo_parameters = self.algorithm.parameters or {}
         valid_keys = set(algo_parameters.keys())
 
-        # checks the user is not trying to set an undeclared parameter
+        # Checks the user is not trying to set an undeclared parameter
         if not user_keys.issubset(valid_keys):
             err_keys = user_keys - valid_keys
             message = "parameters `%s' are not declared for algorithm `%s' - " \
@@ -150,51 +145,97 @@ class Runner(object):
                     exc = self.exc or ValueError
                     raise exc(message)
 
-            else: #user has not set a value, use the default
+            else: # User has not set a value, use the default
                 value = algo_parameters[key]['default']
 
-            # in the end, set the value on the dictionary to be returned
+            # In the end, set the value on the dictionary to be returned
             retval[key] = value
 
         return retval
 
 
-    def setup(self, parameters, *args, **kwargs):
+    def setup(self, parameters):
         '''Sets up the algorithm, only effective on the first call'''
 
+        # Only effective on the first call
         if self.ready:
             return self.ready
 
         completed_parameters = self._check_parameters(parameters)
-        kwargs['parameters'] = completed_parameters
 
-        self.ready = loader.run(self.obj, 'setup', self.exc, *args, **kwargs)
+        self.ready = loader.run(self.obj, 'setup', self.exc, completed_parameters)
         return self.ready
 
 
-    def prepare(self, parameters, *args, **kwargs):
+    def prepare(self, data_loaders):
         '''Let the algorithm process the data on the non-principal channels'''
 
+        # Only effective on the first call
+        if self.prepared:
+            return self.prepared
+
+        # setup() must have run
         if not self.ready:
-            message = "algorithm `%s' is not yet setup" % (self.name,)
             exc = self.exc or RuntimeError
-            raise self.exc(message)
+            raise exc("Algorithm '%s' is not yet setup" % self.name)
 
-        if not hasattr(self.obj, 'prepare'):
+        # Not available in API version 1
+        if self.algorithm.api_version == 1:
+            self.prepared = True
             return True
 
-        return loader.run(self.obj, 'prepare', self.exc, *args, **kwargs)
+        # The method is optional
+        if hasattr(self.obj, 'prepare'):
+            self.prepared = loader.run(self.obj, 'prepare', self.exc, data_loaders)
+        else:
+            self.prepared = True
 
+        return self.prepared
 
-    def process(self, *args, **kwargs):
+
+    def process(self, inputs=None, data_loaders=None, outputs=None, output=None):
         '''Runs through data'''
 
+        exc = self.exc or RuntimeError
+
+        def _check_argument(argument, name):
+            if argument is None:
+                raise exc('Missing argument: %s' % name)
+
+        # setup() must have run
         if not self.ready:
-            message = "algorithm `%s' is not yet setup" % (self.name,)
-            exc = self.exc or RuntimeError
-            raise self.exc(message)
+            raise exc("Algorithm '%s' is not yet setup" % self.name)
 
-        return loader.run(self.obj, 'process', self.exc, *args, **kwargs)
+        # prepare() must have run
+        if not self.prepared:
+            raise exc("Algorithm '%s' is not yet prepared" % self.name)
+
+        # Call the correct version of process()
+        if self.algorithm.isAnalyzer:
+            _check_argument(output, 'output')
+            outputs_to_use = output
+        else:
+            _check_argument(outputs, 'outputs')
+            outputs_to_use = outputs
+
+        if self.algorithm.type == Algorithm.LEGACY:
+            _check_argument(inputs, 'inputs')
+            return loader.run(self.obj, 'process', self.exc, inputs, outputs_to_use)
+
+        else:
+            _check_argument(data_loaders, 'data_loaders')
+
+            if self.algorithm.type == Algorithm.SEQUENTIAL:
+                _check_argument(inputs, 'inputs')
+                return loader.run(self.obj, 'process', self.exc, inputs, data_loaders,
+                                  outputs_to_use)
+
+            elif self.algorithm.type == Algorithm.AUTONOMOUS:
+                return loader.run(self.obj, 'process', self.exc, data_loaders,
+                                  outputs_to_use)
+
+            else:
+                raise exc('Unknown algorithm type: %s' % self.algorithm.type)
 
 
     def __getattr__(self, key):
@@ -568,6 +609,11 @@ class Algorithm(object):
         return value
 
 
+    @property
+    def isAnalyzer(self):
+        return (self.results is not None)
+
+
     @property
     def results(self):
         return self.data.get('results')
@@ -601,6 +647,67 @@ class Algorithm(object):
         return value
 
 
+    @property
+    def description(self):
+        """The short description for this object"""
+        return self.data.get('description', None)
+
+
+    @description.setter
+    def description(self, value):
+        """Sets the short description for this object"""
+        self.data['description'] = value
+
+
+    @property
+    def documentation(self):
+        """The full-length description for this object"""
+
+        if not self._name:
+            raise RuntimeError("algorithm has no name")
+
+        if self.storage.doc.exists():
+            return self.storage.doc.load()
+        return None
+
+
+    @documentation.setter
+    def documentation(self, value):
+        """Sets the full-length description for this object"""
+
+        if not self._name:
+            raise RuntimeError("algorithm has no name")
+
+        if hasattr(value, 'read'):
+            self.storage.doc.save(value.read())
+        else:
+            self.storage.doc.save(value)
+
+
+    def hash(self):
+        """Returns the hexadecimal hash for the current algorithm"""
+
+        if not self._name:
+            raise RuntimeError("algorithm has no name")
+
+        return self.storage.hash()
+
+
+    def result_dataformat(self):
+        """Generates, on-the-fly, the dataformat for the result readout"""
+
+        if not self.results:
+            raise TypeError("algorithm `%s' is a block algorithm, not an analyzer" \
+                    % (self.name))
+
+        format = dataformat.DataFormat(self.prefix,
+                dict([(k, v['type']) for k,v in self.results.items()]))
+
+        format.name = 'analysis:' + self.name
+
+        return format
+
+
     def uses_dict(self):
         """Returns the usage dictionary for all dependent modules"""
 
@@ -665,64 +772,3 @@ class Algorithm(object):
                 raise #just re-raise the user exception
 
         return Runner(self.__module, klass, self, exc)
-
-
-    @property
-    def description(self):
-        """The short description for this object"""
-        return self.data.get('description', None)
-
-
-    @description.setter
-    def description(self, value):
-        """Sets the short description for this object"""
-        self.data['description'] = value
-
-
-    @property
-    def documentation(self):
-        """The full-length description for this object"""
-
-        if not self._name:
-            raise RuntimeError("algorithm has no name")
-
-        if self.storage.doc.exists():
-            return self.storage.doc.load()
-        return None
-
-
-    @documentation.setter
-    def documentation(self, value):
-        """Sets the full-length description for this object"""
-
-        if not self._name:
-            raise RuntimeError("algorithm has no name")
-
-        if hasattr(value, 'read'):
-            self.storage.doc.save(value.read())
-        else:
-            self.storage.doc.save(value)
-
-
-    def hash(self):
-        """Returns the hexadecimal hash for the current algorithm"""
-
-        if not self._name:
-            raise RuntimeError("algorithm has no name")
-
-        return self.storage.hash()
-
-
-    def result_dataformat(self):
-        """Generates, on-the-fly, the dataformat for the result readout"""
-
-        if not self.results:
-            raise TypeError("algorithm `%s' is a block algorithm, not an analyzer" \
-                    % (self.name))
-
-        format = dataformat.DataFormat(self.prefix,
-                dict([(k, v['type']) for k,v in self.results.items()]))
-
-        format.name = 'analysis:' + self.name
-
-        return format
diff --git a/beat/backend/python/data_loaders.py b/beat/backend/python/data_loaders.py
index b1953b3..ecf1a57 100755
--- a/beat/backend/python/data_loaders.py
+++ b/beat/backend/python/data_loaders.py
@@ -323,8 +323,8 @@ class DataLoaderList(object):
             if isinstance(name_or_index, six.string_types):
                 return [x for x in self._loaders if x.channel == name_or_index][0]
 
-            elif isinstance(index, int):
-                return self._loaders[index]
+            elif isinstance(name_or_index, int):
+                return self._loaders[name_or_index]
         except:
             pass
 
@@ -345,3 +345,12 @@ class DataLoaderList(object):
             return [ k for k in self._loaders if input_name in k.input_names() ][0]
         except:
             return None
+
+
+    def secondaries(self):
+        secondaries_list = DataLoaderList()
+        for data_loader in self._loaders:
+            if data_loader is not self.main_loader:
+                secondaries_list.add(data_loader)
+
+        return secondaries_list
diff --git a/beat/backend/python/loader.py b/beat/backend/python/loader.py
old mode 100644
new mode 100755
index c0abeef..9b8efbc
--- a/beat/backend/python/loader.py
+++ b/beat/backend/python/loader.py
@@ -69,6 +69,9 @@ def load_module(name, path, uses):
     return retval
 
 
+#----------------------------------------------------------
+
+
 def run(obj, method, exc=None, *args, **kwargs):
     '''Runs a method on the object and protects its execution
 
@@ -96,7 +99,9 @@ def run(obj, method, exc=None, *args, **kwargs):
     '''
 
     try:
-        if method == '__new__': return obj(*args, **kwargs)
+        if method == '__new__':
+            return obj(*args, **kwargs)
+
         return getattr(obj, method)(*args, **kwargs)
     except Exception as e:
         if exc is not None:
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/add/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/add/1.json
new file mode 100644
index 0000000..0ec3448
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/add/1.json
@@ -0,0 +1,30 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "parameters": {
+        "sync": {
+            "default": "in1",
+            "type": "string"
+        }
+    },
+    "groups": [
+        {
+            "inputs": {
+                "in1": {
+                    "type": "user/single_integer/1"
+                },
+                "in2": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/add/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/add/1.py
new file mode 100755
index 0000000..349c623
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/add/1.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        self.sync = parameters['sync']
+        return True
+
+
+    def process(self, data_loaders, outputs):
+        data_loader = data_loaders.loaderOf('in1')
+
+        for i in range(data_loader.count(self.sync)):
+            view = data_loader.view(self.sync, i)
+
+            (data, start, end) = view[view.count() - 1]
+
+            outputs['out'].write({
+                    'value': data['in1'].value + data['in2'].value,
+                },
+                end
+            )
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/add2/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/add2/1.json
new file mode 100644
index 0000000..966623e
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/add2/1.json
@@ -0,0 +1,28 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "in1": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        },
+        {
+            "inputs": {
+                "in2": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/add2/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/add2/1.py
new file mode 100755
index 0000000..2b82381
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/add2/1.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        data_loader = data_loaders.loaderOf('in2')
+
+        (data, start, end) = data_loader[0]
+        self.offset = data['in2'].value
+
+        return True
+
+
+    def process(self, data_loaders, outputs):
+        data_loader = data_loaders.loaderOf('in1')
+
+        for i in range(data_loader.count()):
+            (data, start, end) = data_loader[i]
+
+            outputs['out'].write({
+                    'value': data['in1'].value + self.offset,
+                },
+                end
+            )
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/add3/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/add3/1.json
new file mode 100644
index 0000000..cefb5e0
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/add3/1.json
@@ -0,0 +1,31 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "value": {
+                    "type": "user/single_integer/1"
+                },
+                "offset_index": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        },
+        {
+            "inputs": {
+                "offset": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/add3/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/add3/1.py
new file mode 100755
index 0000000..c04edf4
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/add3/1.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def process(self, data_loaders, outputs):
+        values = data_loaders.loaderOf('value')
+        offsets = data_loaders.loaderOf('offset')
+
+        for i in range(values.count()):
+            (data, start, end) = values[i]
+            offset_index = data['offset_index'].value
+
+            (data2, start2, end2) = offsets[offset_index]
+
+            outputs['out'].write({
+                    'value': data['value'].value + data2['offset'].value,
+                },
+                end
+            )
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/echo/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/echo/1.json
new file mode 100644
index 0000000..43f7a57
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/echo/1.json
@@ -0,0 +1,21 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "in": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/echo/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/echo/1.py
new file mode 100755
index 0000000..d174603
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/echo/1.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def process(self, data_loaders, outputs):
+        data_loader = data_loaders.loaderOf('in')
+
+        for i in range(data_loader.count()):
+            (data, start, end) = data_loader[i]
+
+            outputs['out'].write({
+                    'value': data['in'].value,
+                },
+                end
+            )
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.json
new file mode 100644
index 0000000..aff2ffc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.py
new file mode 100755
index 0000000..d370eab
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/no_prepare/1.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.json
new file mode 100644
index 0000000..aff2ffc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.py
new file mode 100755
index 0000000..9fd8278
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/no_setup/1.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2017 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.backend.python module of the BEAT platform.   #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        return True
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.json
new file mode 100644
index 0000000..76b1227
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.json
@@ -0,0 +1,279 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ],
+    "parameters": {
+        "int8_value": {
+            "default": "5",
+            "type": "int8",
+            "description": "Some comment"
+        },
+        "int8_range": {
+            "default": "6",
+            "range": [
+                0,
+                "10"
+            ],
+            "type": "int8",
+            "description": "Another comment"
+        },
+        "int8_choices": {
+            "default": "4",
+            "type": "int8",
+            "choice": [
+                "2",
+                "4",
+                "6",
+                "8",
+                "10"
+            ]
+        },
+        "int16_value": {
+            "default": "500",
+            "type": "int16"
+        },
+        "int16_range": {
+            "default": "300",
+            "type": "int16",
+            "range": [
+                "0",
+                "1000"
+            ]
+        },
+        "int16_choices": {
+            "default": "400",
+            "type": "int16",
+            "choice": [
+                "200",
+                "400",
+                "600",
+                "800",
+                "1000"
+            ]
+        },
+        "int16_choices2": {
+            "default": "400",
+            "type": "int16",
+            "choice": [
+                "600",
+                "400"
+            ]
+        },
+        "int32_value": {
+            "default": "500000",
+            "type": "int32"
+        },
+        "int32_range": {
+            "default": "500000",
+            "type": "int32",
+            "range": [
+                "0",
+                "1000000"
+            ]
+        },
+        "int32_choices": {
+            "default": "400000",
+            "type": "int32",
+            "choice": [
+                "200000",
+                "400000",
+                "600000",
+                "800000",
+                "1000000"
+            ]
+        },
+        "int64_value": {
+            "default": "5000000000",
+            "type": "int64"
+        },
+        "int64_range": {
+            "default": "5000000000",
+            "type": "int64",
+            "range": [
+                "0",
+                "10000000000"
+            ]
+        },
+        "int64_choices": {
+            "default": "4000000000",
+            "type": "int64",
+            "choice": [
+                "2000000000",
+                "4000000000",
+                "6000000000",
+                "8000000000",
+                "10000000000"
+            ]
+        },
+        "uint8_value": {
+            "default": "5",
+            "type": "uint8"
+        },
+        "uint8_range": {
+            "default": "6",
+            "type": "uint8",
+            "range": [
+                "0",
+                "10"
+            ]
+        },
+        "uint8_choices": {
+            "default": "4",
+            "type": "uint8",
+            "choice": [
+                "2",
+                "4",
+                "6",
+                "8",
+                "10"
+            ]
+        },
+        "uint16_value": {
+            "default": "500",
+            "type": "uint16"
+        },
+        "uint16_range": {
+            "default": "500",
+            "type": "uint16",
+            "range": [
+                "0",
+                "1000"
+            ]
+        },
+        "uint16_choices": {
+            "default": "400",
+            "type": "uint16",
+            "choice": [
+                "200",
+                "400",
+                "600",
+                "800",
+                "1000"
+            ]
+        },
+        "uint32_value": {
+            "default": "500000",
+            "type": "uint32"
+        },
+        "uint32_range": {
+            "default": "500000",
+            "type": "uint32",
+            "range": [
+                "0",
+                "1000000"
+            ]
+        },
+        "uint32_choices": {
+            "default": "400000",
+            "type": "uint32",
+            "choice": [
+                "200000",
+                "400000",
+                "600000",
+                "800000",
+                "1000000"
+            ]
+        },
+        "uint64_value": {
+            "default": "5000000000",
+            "type": "uint64"
+        },
+        "uint64_range": {
+            "default": "5000000000",
+            "type": "uint64",
+            "range": [
+                "0",
+                "10000000000"
+            ]
+        },
+        "uint64_choices": {
+            "default": "4000000000",
+            "type": "uint64",
+            "choice": [
+                "2000000000",
+                "4000000000",
+                "6000000000",
+                "8000000000",
+                "10000000000"
+            ]
+        },
+        "float32_value": {
+            "default": "0.0",
+            "type": "float32"
+        },
+        "float32_range": {
+            "default": "0.0",
+            "type": "float32",
+            "range": [
+                "-10.0",
+                "10.0"
+            ]
+        },
+        "float32_choices": {
+            "default": "0.0",
+            "type": "float32",
+            "choice": [
+                "-10.0",
+                "-5.0",
+                "0.0",
+                "5.0",
+                "10.0"
+            ]
+        },
+        "float64_value": {
+            "default": "0.0",
+            "type": "float64"
+        },
+        "float64_range": {
+            "default": "0.0",
+            "type": "float64",
+            "range": [
+                "-100.0",
+                "100.0"
+            ]
+        },
+        "float64_choices": {
+            "default": "0.0",
+            "type": "float64",
+            "choice": [
+                -10.0,
+                -5.0,
+                0.0,
+                5.0,
+                10.0
+            ]
+        },
+        "bool_value": {
+            "default": true,
+            "type": "bool"
+        },
+        "string_value": {
+            "default": "choice1",
+            "type": "string",
+            "choice": [
+                "choice1",
+                "choice2",
+                "choice3"
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.py
new file mode 100755
index 0000000..7d14e79
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/parametrized/1.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        self.int8_value      = parameters['int8_value']
+        self.int8_range      = parameters['int8_range']
+        self.int8_choices    = parameters['int8_choices']
+        self.int16_value     = parameters['int16_value']
+        self.int16_range     = parameters['int16_range']
+        self.int16_choices   = parameters['int16_choices']
+        self.int32_value     = parameters['int32_value']
+        self.int32_range     = parameters['int32_range']
+        self.int32_choices   = parameters['int32_choices']
+        self.int64_value     = parameters['int64_value']
+        self.int64_range     = parameters['int64_range']
+        self.int64_choices   = parameters['int64_choices']
+        self.uint8_value     = parameters['uint8_value']
+        self.uint8_range     = parameters['uint8_range']
+        self.uint8_choices   = parameters['uint8_choices']
+        self.uint16_value    = parameters['uint16_value']
+        self.uint16_range    = parameters['uint16_range']
+        self.uint16_choices  = parameters['uint16_choices']
+        self.uint32_value    = parameters['uint32_value']
+        self.uint32_range    = parameters['uint32_range']
+        self.uint32_choices  = parameters['uint32_choices']
+        self.uint64_value    = parameters['uint64_value']
+        self.uint64_range    = parameters['uint64_range']
+        self.uint64_choices  = parameters['uint64_choices']
+        self.float32_value   = parameters['float32_value']
+        self.float32_range   = parameters['float32_range']
+        self.float32_choices = parameters['float32_choices']
+        self.float64_value   = parameters['float64_value']
+        self.float64_range   = parameters['float64_range']
+        self.float64_choices = parameters['float64_choices']
+        self.bool_value      = parameters['bool_value']
+        self.string_value    = parameters['string_value']
+        return True
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.json
new file mode 100644
index 0000000..aff2ffc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.py
old mode 100644
new mode 100755
similarity index 89%
rename from beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.py
rename to beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.py
index b5b0603..31e0108
--- a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.py
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_crash/1.py
@@ -27,13 +27,9 @@
 
 class Algorithm:
 
-    def __init__(self):
-        self.delayed = True
-
-    def process(self, inputs, outputs):
-        if not(self.delayed):
-            outputs['out_data'].write(inputs['in_data'].data)
-
-        self.delayed = not(self.delayed)
+    def prepare(self, data_loaders):
+        a = b
+        return True
 
+    def process(self, data_loaders, outputs):
         return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.json
new file mode 100644
index 0000000..aff2ffc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.py
new file mode 100755
index 0000000..80f1093
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/prepare_fail/1.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        return False
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.json
new file mode 100644
index 0000000..aff2ffc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.py
new file mode 100755
index 0000000..42e46f7
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/setup_crash/1.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        a = b
+        return True
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.json
new file mode 100644
index 0000000..aff2ffc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "autonomous",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.py b/beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.py
new file mode 100755
index 0000000..8d515da
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/autonomous/setup_fail/1.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        return False
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/add/1.json b/beat/backend/python/test/prefix/algorithms/legacy/add/1.json
new file mode 100644
index 0000000..51ba4f2
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/legacy/add/1.json
@@ -0,0 +1,27 @@
+{
+    "language": "python",
+    "splittable": false,
+    "parameters": {
+        "sync": {
+            "default": "in1",
+            "type": "string"
+        }
+    },
+    "groups": [
+        {
+            "inputs": {
+                "in1": {
+                    "type": "user/single_integer/1"
+                },
+                "in2": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/add/1.py b/beat/backend/python/test/prefix/algorithms/legacy/add/1.py
new file mode 100755
index 0000000..6715932
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/legacy/add/1.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        self.sync = parameters['sync']
+        return True
+
+
+    def process(self, inputs, outputs):
+        if inputs[self.sync].isDataUnitDone():
+            outputs['out'].write({
+                'value': inputs['in1'].data.value + inputs['in2'].data.value,
+            })
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/add2/1.json b/beat/backend/python/test/prefix/algorithms/legacy/add2/1.json
new file mode 100644
index 0000000..20715dc
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/legacy/add2/1.json
@@ -0,0 +1,25 @@
+{
+    "language": "python",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "in1": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        },
+        {
+            "inputs": {
+                "in2": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.py b/beat/backend/python/test/prefix/algorithms/legacy/add2/1.py
old mode 100644
new mode 100755
similarity index 87%
rename from beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.py
rename to beat/backend/python/test/prefix/algorithms/legacy/add2/1.py
index 6bf327a..f177cb9
--- a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.py
+++ b/beat/backend/python/test/prefix/algorithms/legacy/add2/1.py
@@ -28,15 +28,16 @@
 class Algorithm:
 
     def __init__(self):
-        self.delayed = True
+        self.offset = None
 
     def process(self, inputs, outputs):
-        if not(self.delayed):
-            outputs['out_data'].write({
-                'value': inputs['in_data1'].data.value + \
-                        inputs['in_data2'].data.value
-                })
+        if self.offset is None:
+            group = inputs.groupOf('in2')
+            group.next()
+            self.offset = group['in2'].data.value
 
-        self.delayed = not(self.delayed)
+        outputs['out'].write({
+            'value': inputs['in1'].data.value + self.offset,
+        })
 
         return True
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/echo/1.json b/beat/backend/python/test/prefix/algorithms/legacy/echo/1.json
new file mode 100644
index 0000000..bb8eb85
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/legacy/echo/1.json
@@ -0,0 +1,18 @@
+{
+    "language": "python",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "in": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.py b/beat/backend/python/test/prefix/algorithms/legacy/echo/1.py
old mode 100644
new mode 100755
similarity index 97%
rename from beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.py
rename to beat/backend/python/test/prefix/algorithms/legacy/echo/1.py
index fe18d9e..abb54cd
--- a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.py
+++ b/beat/backend/python/test/prefix/algorithms/legacy/echo/1.py
@@ -28,5 +28,5 @@
 class Algorithm:
 
     def process(self, inputs, outputs):
-        outputs['out_data'].write(inputs['in_data'].data)
+        outputs['out'].write(inputs['in'].data)
         return True
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.json b/beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.json
new file mode 100644
index 0000000..56a6ac5
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.json
@@ -0,0 +1,22 @@
+{
+  "schema_version": 1,
+  "language": "python",
+  "splittable": false,
+  "groups": [
+    {
+      "inputs": {
+        "images": {
+          "type": "user/2d_array_of_integers/1"
+        },
+        "labels": {
+          "type": "user/floats/1"
+        }
+      },
+      "outputs": {
+        "features": {
+          "type": "user/1d_array_of_integers/1"
+        }
+      }
+    }
+  ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.py b/beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.py
similarity index 92%
rename from beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.py
rename to beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.py
index 148f1d3..f7f6f5b 100644
--- a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.py
+++ b/beat/backend/python/test/prefix/algorithms/legacy/no_setup/1.py
@@ -28,8 +28,4 @@
 class Algorithm:
 
     def process(self, inputs, outputs):
-        outputs['out_data'].write({
-            'value': inputs['in_data1'].data.value + \
-                    inputs['in_data2'].data.value
-            })
         return True
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.json b/beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.json
deleted file mode 100644
index eefa07c..0000000
--- a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__no_delay/1.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "language": "python",
-  "splittable": false,
-  "groups": [
-    {
-      "inputs": {
-        "in_data": {
-          "type": "user/single_integer/1"
-        }
-      },
-      "outputs": {
-        "out_data": {
-          "type": "user/single_integer/1"
-        }
-      }
-    }
-  ]
-}
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.json b/beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.json
deleted file mode 100644
index eefa07c..0000000
--- a/beat/backend/python/test/prefix/algorithms/legacy/one_stream__with_delay/1.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "language": "python",
-  "splittable": false,
-  "groups": [
-    {
-      "inputs": {
-        "in_data": {
-          "type": "user/single_integer/1"
-        }
-      },
-      "outputs": {
-        "out_data": {
-          "type": "user/single_integer/1"
-        }
-      }
-    }
-  ]
-}
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.json b/beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.json
deleted file mode 100644
index 1a269cb..0000000
--- a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__no_delay/1.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "language": "python",
-  "splittable": false,
-  "groups": [
-    {
-      "inputs": {
-        "in_data1": {
-          "type": "user/single_integer/1"
-        },
-        "in_data2": {
-          "type": "user/single_integer/1"
-        }
-      },
-      "outputs": {
-        "out_data": {
-          "type": "user/single_integer/1"
-        }
-      }
-    }
-  ]
-}
diff --git a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.json b/beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.json
deleted file mode 100644
index 1a269cb..0000000
--- a/beat/backend/python/test/prefix/algorithms/legacy/two_streams__with_delay/1.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "language": "python",
-  "splittable": false,
-  "groups": [
-    {
-      "inputs": {
-        "in_data1": {
-          "type": "user/single_integer/1"
-        },
-        "in_data2": {
-          "type": "user/single_integer/1"
-        }
-      },
-      "outputs": {
-        "out_data": {
-          "type": "user/single_integer/1"
-        }
-      }
-    }
-  ]
-}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/add/1.json b/beat/backend/python/test/prefix/algorithms/sequential/add/1.json
new file mode 100644
index 0000000..9f4576d
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/add/1.json
@@ -0,0 +1,30 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "parameters": {
+        "sync": {
+            "default": "in1",
+            "type": "string"
+        }
+    },
+    "groups": [
+        {
+            "inputs": {
+                "in1": {
+                    "type": "user/single_integer/1"
+                },
+                "in2": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/add/1.py b/beat/backend/python/test/prefix/algorithms/sequential/add/1.py
new file mode 100755
index 0000000..a15f151
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/add/1.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        self.sync = parameters['sync']
+        return True
+
+
+    def process(self, inputs, data_loaders, outputs):
+        if inputs[self.sync].isDataUnitDone():
+            outputs['out'].write({
+                'value': inputs['in1'].data.value + inputs['in2'].data.value,
+            })
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/add2/1.json b/beat/backend/python/test/prefix/algorithms/sequential/add2/1.json
new file mode 100644
index 0000000..69d95bf
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/add2/1.json
@@ -0,0 +1,28 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "in1": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        },
+        {
+            "inputs": {
+                "in2": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/add2/1.py b/beat/backend/python/test/prefix/algorithms/sequential/add2/1.py
new file mode 100755
index 0000000..97e4cce
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/add2/1.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        data_loader = data_loaders.loaderOf('in2')
+
+        (data, start, end) = data_loader[0]
+        self.offset = data['in2'].value
+
+        return True
+
+
+    def process(self, inputs, data_loaders, outputs):
+        outputs['out'].write({
+            'value': inputs['in1'].data.value + self.offset,
+        })
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/add3/1.json b/beat/backend/python/test/prefix/algorithms/sequential/add3/1.json
new file mode 100644
index 0000000..a462096
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/add3/1.json
@@ -0,0 +1,31 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "value": {
+                    "type": "user/single_integer/1"
+                },
+                "offset_index": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        },
+        {
+            "inputs": {
+                "offset": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/add3/1.py b/beat/backend/python/test/prefix/algorithms/sequential/add3/1.py
new file mode 100755
index 0000000..48b1f04
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/add3/1.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def process(self, inputs, data_loaders, outputs):
+        offset_index = inputs['offset_index'].data.value
+
+        data_loader = data_loaders.loaderOf('offset')
+        (data, start, end) = data_loader[offset_index]
+
+        outputs['out'].write({
+            'value': inputs['value'].data.value + data['offset'].value,
+        })
+
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/echo/1.json b/beat/backend/python/test/prefix/algorithms/sequential/echo/1.json
new file mode 100644
index 0000000..a3cdc95
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/echo/1.json
@@ -0,0 +1,21 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "in": {
+                    "type": "user/single_integer/1"
+                }
+            },
+            "outputs": {
+                "out": {
+                    "type": "user/single_integer/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/echo/1.py b/beat/backend/python/test/prefix/algorithms/sequential/echo/1.py
new file mode 100755
index 0000000..24e904a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/echo/1.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def process(self, inputs, data_loaders, outputs):
+        outputs['out'].write(inputs['in'].data)
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.json b/beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.json
new file mode 100644
index 0000000..b5f8d0a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.py b/beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.py
new file mode 100755
index 0000000..9d1ec26
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/no_prepare/1.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def process(self, inputs, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.json b/beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.json
new file mode 100644
index 0000000..b5f8d0a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.py b/beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.py
new file mode 100755
index 0000000..9fd8278
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/no_setup/1.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2017 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.backend.python module of the BEAT platform.   #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        return True
+
+    def process(self, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.json b/beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.json
new file mode 100644
index 0000000..7041137
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.json
@@ -0,0 +1,279 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ],
+    "parameters": {
+        "int8_value": {
+            "default": "5",
+            "type": "int8",
+            "description": "Some comment"
+        },
+        "int8_range": {
+            "default": "6",
+            "range": [
+                0,
+                "10"
+            ],
+            "type": "int8",
+            "description": "Another comment"
+        },
+        "int8_choices": {
+            "default": "4",
+            "type": "int8",
+            "choice": [
+                "2",
+                "4",
+                "6",
+                "8",
+                "10"
+            ]
+        },
+        "int16_value": {
+            "default": "500",
+            "type": "int16"
+        },
+        "int16_range": {
+            "default": "300",
+            "type": "int16",
+            "range": [
+                "0",
+                "1000"
+            ]
+        },
+        "int16_choices": {
+            "default": "400",
+            "type": "int16",
+            "choice": [
+                "200",
+                "400",
+                "600",
+                "800",
+                "1000"
+            ]
+        },
+        "int16_choices2": {
+            "default": "400",
+            "type": "int16",
+            "choice": [
+                "600",
+                "400"
+            ]
+        },
+        "int32_value": {
+            "default": "500000",
+            "type": "int32"
+        },
+        "int32_range": {
+            "default": "500000",
+            "type": "int32",
+            "range": [
+                "0",
+                "1000000"
+            ]
+        },
+        "int32_choices": {
+            "default": "400000",
+            "type": "int32",
+            "choice": [
+                "200000",
+                "400000",
+                "600000",
+                "800000",
+                "1000000"
+            ]
+        },
+        "int64_value": {
+            "default": "5000000000",
+            "type": "int64"
+        },
+        "int64_range": {
+            "default": "5000000000",
+            "type": "int64",
+            "range": [
+                "0",
+                "10000000000"
+            ]
+        },
+        "int64_choices": {
+            "default": "4000000000",
+            "type": "int64",
+            "choice": [
+                "2000000000",
+                "4000000000",
+                "6000000000",
+                "8000000000",
+                "10000000000"
+            ]
+        },
+        "uint8_value": {
+            "default": "5",
+            "type": "uint8"
+        },
+        "uint8_range": {
+            "default": "6",
+            "type": "uint8",
+            "range": [
+                "0",
+                "10"
+            ]
+        },
+        "uint8_choices": {
+            "default": "4",
+            "type": "uint8",
+            "choice": [
+                "2",
+                "4",
+                "6",
+                "8",
+                "10"
+            ]
+        },
+        "uint16_value": {
+            "default": "500",
+            "type": "uint16"
+        },
+        "uint16_range": {
+            "default": "500",
+            "type": "uint16",
+            "range": [
+                "0",
+                "1000"
+            ]
+        },
+        "uint16_choices": {
+            "default": "400",
+            "type": "uint16",
+            "choice": [
+                "200",
+                "400",
+                "600",
+                "800",
+                "1000"
+            ]
+        },
+        "uint32_value": {
+            "default": "500000",
+            "type": "uint32"
+        },
+        "uint32_range": {
+            "default": "500000",
+            "type": "uint32",
+            "range": [
+                "0",
+                "1000000"
+            ]
+        },
+        "uint32_choices": {
+            "default": "400000",
+            "type": "uint32",
+            "choice": [
+                "200000",
+                "400000",
+                "600000",
+                "800000",
+                "1000000"
+            ]
+        },
+        "uint64_value": {
+            "default": "5000000000",
+            "type": "uint64"
+        },
+        "uint64_range": {
+            "default": "5000000000",
+            "type": "uint64",
+            "range": [
+                "0",
+                "10000000000"
+            ]
+        },
+        "uint64_choices": {
+            "default": "4000000000",
+            "type": "uint64",
+            "choice": [
+                "2000000000",
+                "4000000000",
+                "6000000000",
+                "8000000000",
+                "10000000000"
+            ]
+        },
+        "float32_value": {
+            "default": "0.0",
+            "type": "float32"
+        },
+        "float32_range": {
+            "default": "0.0",
+            "type": "float32",
+            "range": [
+                "-10.0",
+                "10.0"
+            ]
+        },
+        "float32_choices": {
+            "default": "0.0",
+            "type": "float32",
+            "choice": [
+                "-10.0",
+                "-5.0",
+                "0.0",
+                "5.0",
+                "10.0"
+            ]
+        },
+        "float64_value": {
+            "default": "0.0",
+            "type": "float64"
+        },
+        "float64_range": {
+            "default": "0.0",
+            "type": "float64",
+            "range": [
+                "-100.0",
+                "100.0"
+            ]
+        },
+        "float64_choices": {
+            "default": "0.0",
+            "type": "float64",
+            "choice": [
+                -10.0,
+                -5.0,
+                0.0,
+                5.0,
+                10.0
+            ]
+        },
+        "bool_value": {
+            "default": true,
+            "type": "bool"
+        },
+        "string_value": {
+            "default": "choice1",
+            "type": "string",
+            "choice": [
+                "choice1",
+                "choice2",
+                "choice3"
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.py b/beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.py
new file mode 100755
index 0000000..544cf2a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/parametrized/1.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        self.int8_value      = parameters['int8_value']
+        self.int8_range      = parameters['int8_range']
+        self.int8_choices    = parameters['int8_choices']
+        self.int16_value     = parameters['int16_value']
+        self.int16_range     = parameters['int16_range']
+        self.int16_choices   = parameters['int16_choices']
+        self.int32_value     = parameters['int32_value']
+        self.int32_range     = parameters['int32_range']
+        self.int32_choices   = parameters['int32_choices']
+        self.int64_value     = parameters['int64_value']
+        self.int64_range     = parameters['int64_range']
+        self.int64_choices   = parameters['int64_choices']
+        self.uint8_value     = parameters['uint8_value']
+        self.uint8_range     = parameters['uint8_range']
+        self.uint8_choices   = parameters['uint8_choices']
+        self.uint16_value    = parameters['uint16_value']
+        self.uint16_range    = parameters['uint16_range']
+        self.uint16_choices  = parameters['uint16_choices']
+        self.uint32_value    = parameters['uint32_value']
+        self.uint32_range    = parameters['uint32_range']
+        self.uint32_choices  = parameters['uint32_choices']
+        self.uint64_value    = parameters['uint64_value']
+        self.uint64_range    = parameters['uint64_range']
+        self.uint64_choices  = parameters['uint64_choices']
+        self.float32_value   = parameters['float32_value']
+        self.float32_range   = parameters['float32_range']
+        self.float32_choices = parameters['float32_choices']
+        self.float64_value   = parameters['float64_value']
+        self.float64_range   = parameters['float64_range']
+        self.float64_choices = parameters['float64_choices']
+        self.bool_value      = parameters['bool_value']
+        self.string_value    = parameters['string_value']
+        return True
+
+    def process(self, inputs, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.json b/beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.json
new file mode 100644
index 0000000..b5f8d0a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.py b/beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.py
new file mode 100755
index 0000000..774b49e
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/prepare_crash/1.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        a = b
+        return True
+
+    def process(self, inputs, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.json b/beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.json
new file mode 100644
index 0000000..b5f8d0a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.py b/beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.py
new file mode 100755
index 0000000..6a0f92b
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/prepare_fail/1.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def prepare(self, data_loaders):
+        return False
+
+    def process(self, inputs, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.json b/beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.json
new file mode 100644
index 0000000..b5f8d0a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.py b/beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.py
new file mode 100755
index 0000000..20bd342
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/setup_crash/1.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        a = b
+        return True
+
+    def process(self, inputs, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.json b/beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.json
new file mode 100644
index 0000000..b5f8d0a
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.json
@@ -0,0 +1,24 @@
+{
+    "schema_version": 2,
+    "language": "python",
+    "api_version": 2,
+    "type": "sequential",
+    "splittable": false,
+    "groups": [
+        {
+            "inputs": {
+                "images": {
+                    "type": "user/2d_array_of_integers/1"
+                },
+                "labels": {
+                    "type": "user/floats/1"
+                }
+            },
+            "outputs": {
+                "features": {
+                    "type": "user/1d_array_of_integers/1"
+                }
+            }
+        }
+    ]
+}
diff --git a/beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.py b/beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.py
new file mode 100755
index 0000000..e9a2814
--- /dev/null
+++ b/beat/backend/python/test/prefix/algorithms/sequential/setup_fail/1.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.core module of the BEAT platform.             #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+class Algorithm:
+
+    def setup(self, parameters):
+        return False
+
+    def process(self, inputs, data_loaders, outputs):
+        return True
diff --git a/beat/backend/python/test/test_algorithm.py b/beat/backend/python/test/test_algorithm.py
index 6bac5b7..2d84296 100644
--- a/beat/backend/python/test/test_algorithm.py
+++ b/beat/backend/python/test/test_algorithm.py
@@ -26,11 +26,28 @@
 ###############################################################################
 
 
-import six
 import unittest
+import six
+import os
+import glob
+import tempfile
+import numpy as np
 
 from ..algorithm import Algorithm
-
+from ..data_loaders import DataLoaderList
+from ..data_loaders import DataLoader
+from ..dataformat import DataFormat
+from ..data import CachedDataSink
+from ..data import CachedDataSource
+from ..data import CachedFileLoader
+from ..inputs import Input
+from ..inputs import InputGroup
+from ..inputs import InputList
+from ..outputs import Output
+from ..outputs import OutputList
+from ..outputs import SynchronizationListener
+
+from .mocks import MockDataSink
 from . import prefix
 
 
@@ -85,6 +102,7 @@ class TestLegacyAPI_Loading(unittest.TestCase):
 
         runnable = algorithm.runner()
         self.assertTrue(runnable.ready) #no setup
+        self.assertTrue(runnable.prepared)
 
 
     def test_load_valid_analyser(self):
@@ -111,6 +129,7 @@ class TestLegacyAPI_Loading(unittest.TestCase):
 
         runnable = algorithm.runner()
         self.assertTrue(runnable.ready) #no setup
+        self.assertTrue(runnable.prepared)
 
 
     def test_hash(self):
@@ -176,6 +195,7 @@ class TestSequentialAPI_Loading(unittest.TestCase):
 
         runnable = algorithm.runner()
         self.assertFalse(runnable.ready)
+        self.assertFalse(runnable.prepared)
 
 
     def test_load_valid_analyser(self):
@@ -202,6 +222,7 @@ class TestSequentialAPI_Loading(unittest.TestCase):
 
         runnable = algorithm.runner()
         self.assertFalse(runnable.ready)
+        self.assertFalse(runnable.prepared)
 
 
 #----------------------------------------------------------
@@ -225,6 +246,7 @@ class TestAutonomousAPI_Loading(unittest.TestCase):
 
         runnable = algorithm.runner()
         self.assertFalse(runnable.ready)
+        self.assertFalse(runnable.prepared)
 
 
     def test_load_valid_analyser(self):
@@ -251,6 +273,7 @@ class TestAutonomousAPI_Loading(unittest.TestCase):
 
         runnable = algorithm.runner()
         self.assertFalse(runnable.ready)
+        self.assertFalse(runnable.prepared)
 
 
 #----------------------------------------------------------
@@ -258,8 +281,13 @@ class TestAutonomousAPI_Loading(unittest.TestCase):
 
 class TestLegacyAPI_Setup(unittest.TestCase):
 
+    def __init__(self, methodName='runTest'):
+        super(TestLegacyAPI_Setup, self).__init__(methodName)
+        self.username = 'legacy'
+
+
     def test_setup_crashing_algorithm(self):
-        algorithm = Algorithm(prefix, 'legacy/setup_crash/1')
+        algorithm = Algorithm(prefix, self.username + '/setup_crash/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -270,7 +298,7 @@ class TestLegacyAPI_Setup(unittest.TestCase):
 
 
     def test_setup_failing_algorithm(self):
-        algorithm = Algorithm(prefix, 'legacy/setup_fail/1')
+        algorithm = Algorithm(prefix, self.username + '/setup_fail/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -280,7 +308,7 @@ class TestLegacyAPI_Setup(unittest.TestCase):
 
 
     def test_setup_nonparametrized_algorithm(self):
-        algorithm = Algorithm(prefix, 'legacy/valid_algorithm/1')
+        algorithm = Algorithm(prefix, self.username + '/no_setup/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -290,7 +318,7 @@ class TestLegacyAPI_Setup(unittest.TestCase):
 
 
     def test_setup_parametrized_algorithm_default_values(self):
-        algorithm = Algorithm(prefix, 'legacy/parametrized/1')
+        algorithm = Algorithm(prefix, self.username + '/parametrized/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -323,7 +351,7 @@ class TestLegacyAPI_Setup(unittest.TestCase):
 
 
     def test_setup_parametrized_algorithm_customized_values(self):
-        algorithm = Algorithm(prefix, 'legacy/parametrized/1')
+        algorithm = Algorithm(prefix, self.username + '/parametrized/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -364,7 +392,7 @@ class TestLegacyAPI_Setup(unittest.TestCase):
 
 
     def test_setup_parametrized_algorithm_out_of_range_value(self):
-        algorithm = Algorithm(prefix, 'legacy/parametrized/1')
+        algorithm = Algorithm(prefix, self.username + '/parametrized/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -377,7 +405,7 @@ class TestLegacyAPI_Setup(unittest.TestCase):
 
 
     def test_setup_parametrized_algorithm_invalid_choice_value(self):
-        algorithm = Algorithm(prefix, 'legacy/parametrized/1')
+        algorithm = Algorithm(prefix, self.username + '/parametrized/1')
         self.assertTrue(algorithm.valid)
 
         runnable = algorithm.runner()
@@ -387,3 +415,805 @@ class TestLegacyAPI_Setup(unittest.TestCase):
             self.assertTrue(runnable.setup(dict(
                 string_value='unknown',
             )))
+
+
+#----------------------------------------------------------
+
+
+class TestSequentialAPI_Setup(TestLegacyAPI_Setup):
+
+    def __init__(self, methodName='runTest'):
+        super(TestSequentialAPI_Setup, self).__init__(methodName)
+        self.username = 'sequential'
+
+
+#----------------------------------------------------------
+
+
+class TestAutonomousAPI_Setup(TestLegacyAPI_Setup):
+
+    def __init__(self, methodName='runTest'):
+        super(TestAutonomousAPI_Setup, self).__init__(methodName)
+        self.username = 'sequential'
+
+
+#----------------------------------------------------------
+
+
+class TestLegacyAPI_Prepare(unittest.TestCase):
+
+    def test_always_prepared(self):
+        algorithm = Algorithm(prefix, 'legacy/valid_algorithm/1')
+        self.assertTrue(algorithm.valid)
+
+        runnable = algorithm.runner()
+        self.assertTrue(runnable.prepared)
+        self.assertTrue(runnable.prepare(DataLoaderList()))
+        self.assertTrue(runnable.prepared)
+
+
+#----------------------------------------------------------
+
+
+class TestSequentialAPI_Prepare(unittest.TestCase):
+
+    def __init__(self, methodName='runTest'):
+        super(TestSequentialAPI_Prepare, self).__init__(methodName)
+        self.username = 'sequential'
+
+
+    def test_prepare_crashing_algorithm(self):
+        algorithm = Algorithm(prefix, self.username + '/prepare_crash/1')
+        self.assertTrue(algorithm.valid)
+
+        runnable = algorithm.runner()
+        self.assertTrue(runnable.ready)
+
+        self.assertFalse(runnable.prepared)
+
+        with self.assertRaises(NameError):
+            runnable.prepare(DataLoaderList())
+
+
+    def test_prepare_failing_algorithm(self):
+        algorithm = Algorithm(prefix, self.username + '/prepare_fail/1')
+        self.assertTrue(algorithm.valid)
+
+        runnable = algorithm.runner()
+        self.assertTrue(runnable.ready)
+
+        self.assertFalse(runnable.prepared)
+        self.assertFalse(runnable.prepare(DataLoaderList()))
+        self.assertFalse(runnable.prepared)
+
+
+    def test_no_preparation_algorithm(self):
+        algorithm = Algorithm(prefix, self.username + '/no_prepare/1')
+        self.assertTrue(algorithm.valid)
+
+        runnable = algorithm.runner()
+        self.assertTrue(runnable.ready)
+
+        self.assertTrue(runnable.prepared)
+        self.assertTrue(runnable.prepare(DataLoaderList()))
+        self.assertTrue(runnable.prepared)
+
+
+#----------------------------------------------------------
+
+
+class TestAutonomousAPI_Prepare(TestSequentialAPI_Prepare):
+
+    def __init__(self, methodName='runTest'):
+        super(TestAutonomousAPI_Prepare, self).__init__(methodName)
+        self.username = 'autonomous'
+
+
+#----------------------------------------------------------
+
+
+class TestExecutionBase(unittest.TestCase):
+
+    def setUp(self):
+        self.filenames = {}
+
+
+    def tearDown(self):
+        for f in self.filenames.values():
+            basename, ext = os.path.splitext(f)
+            filenames = [f]
+            filenames += glob.glob(basename + '*')
+            for filename in filenames:
+                if os.path.exists(filename):
+                    os.unlink(filename)
+
+
+    def writeData(self, input_name, indices, start_value):
+        testfile = tempfile.NamedTemporaryFile(prefix=__name__ + input_name, suffix='.data')
+        testfile.close() # preserve only the name
+        self.filenames[input_name] = testfile.name
+
+        dataformat = DataFormat(prefix, 'user/single_integer/1')
+        self.assertTrue(dataformat.valid)
+
+        data_sink = CachedDataSink()
+        self.assertTrue(data_sink.setup(self.filenames[input_name], dataformat))
+
+        for i in indices:
+            data = dataformat.type()
+            data.value = np.int32(start_value + i[0])
+            data_sink.write(data, i[0], i[1])
+
+        (nb_bytes, duration) = data_sink.statistics()
+        self.assertTrue(nb_bytes > 0)
+        self.assertTrue(duration > 0)
+
+        data_sink.close()
+        del data_sink
+
+
+#----------------------------------------------------------
+
+
+class TestLegacyAPI_Process(TestExecutionBase):
+
+    def create_io(self, infos):
+        dataformat = DataFormat(prefix, 'user/single_integer/1')
+
+        synchronization_listener = SynchronizationListener()
+
+        # Inputs
+        inputs = InputList()
+
+        for group_name, group_inputs in infos.items():
+            if len(inputs) == 0:
+                group = InputGroup(group_name, synchronization_listener, True)
+            else:
+                group = InputGroup(group_name, None, False)
+
+            inputs.add(group)
+
+            for input_name in group_inputs:
+                data_source = CachedDataSource()
+                data_source.setup(self.filenames[input_name], prefix)
+                group.add(Input(input_name, dataformat, data_source))
+
+        # Outputs
+        outputs = OutputList()
+
+        data_sink = MockDataSink(dataformat)
+        outputs.add(Output('out', data_sink, synchronization_listener))
+
+        return (inputs, outputs, data_sink)
+
+
+    def test_one_group_of_one_input(self):
+        self.writeData('in', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+
+        (inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'legacy/echo/1')
+        runner = algorithm.runner()
+
+        group = inputs.main_group
+
+        while group.hasMoreData():
+            group.restricted_access = False
+            group.next()
+            group.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        for i in range(0, 4):
+            self.assertEqual(data_sink.written[i].start, i)
+            self.assertEqual(data_sink.written[i].end, i)
+            self.assertEqual(data_sink.written[i].data.value, 1000 + i)
+
+
+    def test_one_group_of_two_inputs_1(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 1), (2, 3)], 2000)
+
+        (inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'legacy/add/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.setup({
+            'sync': 'in1',
+        }))
+
+        group = inputs.main_group
+
+        while group.hasMoreData():
+            group.restricted_access = False
+            group.next()
+            group.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 3000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 3004)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3005)
+
+
+    def test_one_group_of_two_inputs_2(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 1), (2, 3)], 2000)
+
+        (inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'legacy/add/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.setup({
+            'sync': 'in2',
+        }))
+
+        group = inputs.main_group
+
+        while group.hasMoreData():
+            group.restricted_access = False
+            group.next()
+            group.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 2)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3005)
+
+
+    def test_two_groups_of_one_input(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 3)], 2000)
+
+        (inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+            ],
+            'group2': [
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'legacy/add2/1')
+        runner = algorithm.runner()
+
+        group = inputs.main_group
+
+        while group.hasMoreData():
+            group.restricted_access = False
+            group.next()
+            group.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 3000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 3002)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3003)
+
+
+#----------------------------------------------------------
+
+
+class TestSequentialAPI_Process(TestExecutionBase):
+
+    def create_io(self, infos):
+        dataformat = DataFormat(prefix, 'user/single_integer/1')
+
+        synchronization_listener = SynchronizationListener()
+
+        # Data loaders & inputs
+        inputs = None
+        data_loaders = DataLoaderList()
+
+        for group_name, group_inputs in infos.items():
+            if inputs is None:
+                inputs = InputGroup(group_name, synchronization_listener, True)
+
+                for input_name in group_inputs:
+                    data_source = CachedDataSource()
+                    data_source.setup(self.filenames[input_name], prefix)
+                    inputs.add(Input(input_name, dataformat, data_source))
+
+            else:
+                data_loader = DataLoader(group_name)
+                data_loaders.add(data_loader)
+
+                for input_name in group_inputs:
+                    cached_file = CachedFileLoader()
+                    cached_file.setup(self.filenames[input_name], prefix)
+                    data_loader.add(input_name, cached_file)
+
+        # Outputs
+        outputs = OutputList()
+
+        data_sink = MockDataSink(dataformat)
+        outputs.add(Output('out', data_sink, synchronization_listener))
+
+        return (data_loaders, inputs, outputs, data_sink)
+
+
+    def test_one_group_of_one_input(self):
+        self.writeData('in', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+
+        (data_loaders, inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'sequential/echo/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders))
+
+        while inputs.hasMoreData():
+            inputs.restricted_access = False
+            inputs.next()
+            inputs.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        for i in range(0, 4):
+            self.assertEqual(data_sink.written[i].start, i)
+            self.assertEqual(data_sink.written[i].end, i)
+            self.assertEqual(data_sink.written[i].data.value, 1000 + i)
+
+
+    def test_one_group_of_two_inputs_1(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 1), (2, 3)], 2000)
+
+        (data_loaders, inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'sequential/add/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.setup({
+            'sync': 'in1',
+        }))
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders))
+
+        while inputs.hasMoreData():
+            inputs.restricted_access = False
+            inputs.next()
+            inputs.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 3000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 3004)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3005)
+
+
+    def test_one_group_of_two_inputs_2(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 1), (2, 3)], 2000)
+
+        (data_loaders, inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'sequential/add/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.setup({
+            'sync': 'in2',
+        }))
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders))
+
+        while inputs.hasMoreData():
+            inputs.restricted_access = False
+            inputs.next()
+            inputs.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 2)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3005)
+
+
+    def test_two_groups_of_one_input(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 3)], 2000)
+
+        (data_loaders, inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+            ],
+            'group2': [
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'sequential/add2/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders))
+
+        while inputs.hasMoreData():
+            inputs.restricted_access = False
+            inputs.next()
+            inputs.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 3000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 3002)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3003)
+
+
+    def test_two_groups_of_related_inputs(self):
+        self.writeData('value', [(0, 0), (1, 1), (2, 2), (3, 3)], 0)        # 0, 1, 2, 3
+        self.writeData('offset_index', [(0, 0), (1, 2), (3, 3)], 0)         # 0, 1, 3
+        self.writeData('offset', [(0, 0), (1, 5), (6, 10), (11, 11)], 2000) # 2000, 2001, 2006, 2011
+
+        (data_loaders, inputs, outputs, data_sink) = self.create_io({
+            'group1': [
+                'value',
+                'offset_index',
+            ],
+            'group2': [
+                'offset',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'sequential/add3/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders))
+
+        while inputs.hasMoreData():
+            inputs.restricted_access = False
+            inputs.next()
+            inputs.restricted_access = True
+            self.assertTrue(runner.process(inputs=inputs, data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 2000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 2002)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 2003)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 2014)
+
+
+#----------------------------------------------------------
+
+
+class TestAutonomousAPI_Process(TestExecutionBase):
+
+    def create_io(self, infos):
+        dataformat = DataFormat(prefix, 'user/single_integer/1')
+
+        # Data loaders
+        data_loaders = DataLoaderList()
+
+        for group_name, group_inputs in infos.items():
+            data_loader = DataLoader(group_name)
+            data_loaders.add(data_loader)
+
+            for input_name in group_inputs:
+                cached_file = CachedFileLoader()
+                cached_file.setup(self.filenames[input_name], prefix)
+                data_loader.add(input_name, cached_file)
+
+        # Outputs
+        outputs = OutputList()
+
+        data_sink = MockDataSink(dataformat)
+        outputs.add(Output('out', data_sink))
+
+        return (data_loaders, outputs, data_sink)
+
+
+    def test_one_group_of_one_input(self):
+        self.writeData('in', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+
+        (data_loaders, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'autonomous/echo/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders.secondaries()))
+        self.assertTrue(runner.process(data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        for i in range(0, 4):
+            self.assertEqual(data_sink.written[i].start, i)
+            self.assertEqual(data_sink.written[i].end, i)
+            self.assertEqual(data_sink.written[i].data.value, 1000 + i)
+
+
+    def test_one_group_of_two_inputs_1(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 1), (2, 3)], 2000)
+
+        (data_loaders, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'autonomous/add/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.setup({
+            'sync': 'in1',
+        }))
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders.secondaries()))
+        self.assertTrue(runner.process(data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 3000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 3004)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3005)
+
+
+    def test_one_group_of_two_inputs_2(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 1), (2, 3)], 2000)
+
+        (data_loaders, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'autonomous/add/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.setup({
+            'sync': 'in2',
+        }))
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders.secondaries()))
+        self.assertTrue(runner.process(data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 2)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3005)
+
+
+    def test_two_groups_of_one_input(self):
+        self.writeData('in1', [(0, 0), (1, 1), (2, 2), (3, 3)], 1000)
+        self.writeData('in2', [(0, 3)], 2000)
+
+        (data_loaders, outputs, data_sink) = self.create_io({
+            'group1': [
+                'in1',
+            ],
+            'group2': [
+                'in2',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'autonomous/add2/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders.secondaries()))
+        self.assertTrue(runner.process(data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 3000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 3001)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 3002)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 3003)
+
+
+    def test_two_groups_of_related_inputs(self):
+        self.writeData('value', [(0, 0), (1, 1), (2, 2), (3, 3)], 0)        # 0, 1, 2, 3
+        self.writeData('offset_index', [(0, 0), (1, 2), (3, 3)], 0)         # 0, 1, 3
+        self.writeData('offset', [(0, 0), (1, 5), (6, 10), (11, 11)], 2000) # 2000, 2001, 2006, 2011
+
+        (data_loaders, outputs, data_sink) = self.create_io({
+            'group1': [
+                'value',
+                'offset_index',
+            ],
+            'group2': [
+                'offset',
+            ],
+        })
+
+        algorithm = Algorithm(prefix, 'autonomous/add3/1')
+        runner = algorithm.runner()
+
+        self.assertTrue(runner.prepare(data_loaders=data_loaders.secondaries()))
+        self.assertTrue(runner.process(data_loaders=data_loaders, outputs=outputs))
+
+        self.assertEqual(len(data_sink.written), 4)
+
+        data_unit = data_sink.written[0]
+        self.assertEqual(data_unit.start, 0)
+        self.assertEqual(data_unit.end, 0)
+        self.assertEqual(data_unit.data.value, 2000)
+
+        data_unit = data_sink.written[1]
+        self.assertEqual(data_unit.start, 1)
+        self.assertEqual(data_unit.end, 1)
+        self.assertEqual(data_unit.data.value, 2002)
+
+        data_unit = data_sink.written[2]
+        self.assertEqual(data_unit.start, 2)
+        self.assertEqual(data_unit.end, 2)
+        self.assertEqual(data_unit.data.value, 2003)
+
+        data_unit = data_sink.written[3]
+        self.assertEqual(data_unit.start, 3)
+        self.assertEqual(data_unit.end, 3)
+        self.assertEqual(data_unit.data.value, 2014)
diff --git a/beat/backend/python/test/test_data_loaders.py b/beat/backend/python/test/test_data_loaders.py
index b784bb7..6823fdf 100644
--- a/beat/backend/python/test/test_data_loaders.py
+++ b/beat/backend/python/test/test_data_loaders.py
@@ -33,6 +33,7 @@ import tempfile
 import numpy as np
 
 from ..data_loaders import DataLoader
+from ..data_loaders import DataLoaderList
 from ..dataformat import DataFormat
 from ..data import CachedDataSink
 from ..data import CachedFileLoader
@@ -43,7 +44,7 @@ from . import prefix
 #----------------------------------------------------------
 
 
-class DataLoaderTest(unittest.TestCase):
+class DataLoaderBaseTest(unittest.TestCase):
 
     def setUp(self):
         self.filenames = {}
@@ -83,6 +84,11 @@ class DataLoaderTest(unittest.TestCase):
         del data_sink
 
 
+#----------------------------------------------------------
+
+
+class DataLoaderTest(DataLoaderBaseTest):
+
     def test_creation(self):
         data_loader = DataLoader('channel1')
 
@@ -647,3 +653,116 @@ class DataLoaderTest(unittest.TestCase):
         view = data_loader.view('input2', 2)
         self.assertTrue(view is None)
 
+
+#----------------------------------------------------------
+
+
+class DataLoaderListTest(DataLoaderBaseTest):
+
+    def test_creation(self):
+        data_loaders = DataLoaderList()
+
+        self.assertTrue(data_loaders.main_loader is None)
+        self.assertEqual(len(data_loaders), 0)
+
+
+    def test_list_unkown_loader_retrieval(self):
+        data_loaders = DataLoaderList()
+        self.assertTrue(data_loaders['unknown'] is None)
+
+
+    def test_list_invalid_index_retrieval(self):
+        data_loaders = DataLoaderList()
+        self.assertTrue(data_loaders[10] is None)
+
+
+    def test_list_loader_of_unknown_input_retrieval(self):
+        data_loaders = DataLoaderList()
+        self.assertTrue(data_loaders.loaderOf('unknown') is None)
+
+
+    def test_list_one_loader_one_input(self):
+        self.writeData('input1', [(0, 0), (1, 1), (2, 2)], 1000)
+
+        data_loader = DataLoader('channel1')
+
+        cached_file = CachedFileLoader()
+        cached_file.setup(self.filenames['input1'], prefix)
+        data_loader.add('input1', cached_file)
+
+        data_loaders = DataLoaderList()
+        data_loaders.add(data_loader)
+
+        self.assertEqual(data_loaders.main_loader, data_loader)
+        self.assertEqual(len(data_loaders), 1)
+
+        self.assertEqual(data_loaders['channel1'], data_loader)
+        self.assertEqual(data_loaders[0], data_loader)
+
+        self.assertEqual(data_loaders.loaderOf('input1'), data_loader)
+
+
+    def test_list_one_loader_two_inputs(self):
+        self.writeData('input1', [(0, 0), (1, 1), (2, 2)], 1000)
+        self.writeData('input2', [(0, 2)], 2000)
+
+        data_loader = DataLoader('channel1')
+
+        cached_file = CachedFileLoader()
+        cached_file.setup(self.filenames['input1'], prefix)
+        data_loader.add('input1', cached_file)
+
+        cached_file = CachedFileLoader()
+        cached_file.setup(self.filenames['input2'], prefix)
+        data_loader.add('input2', cached_file)
+
+        data_loaders = DataLoaderList()
+        data_loaders.add(data_loader)
+
+        self.assertEqual(data_loaders.main_loader, data_loader)
+        self.assertEqual(len(data_loaders), 1)
+
+        self.assertEqual(data_loaders['channel1'], data_loader)
+        self.assertEqual(data_loaders[0], data_loader)
+
+        self.assertEqual(data_loaders.loaderOf('input1'), data_loader)
+        self.assertEqual(data_loaders.loaderOf('input2'), data_loader)
+
+
+    def test_list_two_loaders_three_inputs(self):
+        self.writeData('input1', [(0, 0), (1, 1), (2, 2)], 1000)
+        self.writeData('input2', [(0, 2)], 2000)
+        self.writeData('input3', [(0, 1), (2, 2)], 3000)
+
+        data_loader1 = DataLoader('channel1')
+
+        cached_file = CachedFileLoader()
+        cached_file.setup(self.filenames['input1'], prefix)
+        data_loader1.add('input1', cached_file)
+
+        cached_file = CachedFileLoader()
+        cached_file.setup(self.filenames['input2'], prefix)
+        data_loader1.add('input2', cached_file)
+
+        data_loader2 = DataLoader('channel2')
+
+        cached_file = CachedFileLoader()
+        cached_file.setup(self.filenames['input3'], prefix)
+        data_loader2.add('input3', cached_file)
+
+        data_loaders = DataLoaderList()
+        data_loaders.add(data_loader1)
+        data_loaders.add(data_loader2)
+
+        self.assertEqual(data_loaders.main_loader, data_loader1)
+        self.assertEqual(len(data_loaders), 2)
+
+        self.assertEqual(data_loaders['channel1'], data_loader1)
+        self.assertEqual(data_loaders['channel2'], data_loader2)
+
+        self.assertEqual(data_loaders[0], data_loader1)
+        self.assertEqual(data_loaders[1], data_loader2)
+
+        self.assertEqual(data_loaders.loaderOf('input1'), data_loader1)
+        self.assertEqual(data_loaders.loaderOf('input2'), data_loader1)
+        self.assertEqual(data_loaders.loaderOf('input3'), data_loader2)
diff --git a/beat/backend/python/test/test_inputs.py b/beat/backend/python/test/test_inputs.py
index 5d51fbd..04dc89f 100644
--- a/beat/backend/python/test/test_inputs.py
+++ b/beat/backend/python/test/test_inputs.py
@@ -741,7 +741,7 @@ class InputListTest(unittest.TestCase):
         self.assertTrue(inputs['unknown'] is None)
 
 
-    def test_list_group_of_unkown_input_retrieval(self):
+    def test_list_group_of_unknown_input_retrieval(self):
         inputs = InputList()
 
         self.assertTrue(inputs.groupOf('unknown') is None)
diff --git a/beat/backend/python/test/test_synchronization.py b/beat/backend/python/test/test_synchronization.py
deleted file mode 100644
index df592e8..0000000
--- a/beat/backend/python/test/test_synchronization.py
+++ /dev/null
@@ -1,424 +0,0 @@
-#!/usr/bin/env python
-# vim: set fileencoding=utf-8 :
-
-###############################################################################
-#                                                                             #
-# Copyright (c) 2017 Idiap Research Institute, http://www.idiap.ch/           #
-# Contact: beat.support@idiap.ch                                              #
-#                                                                             #
-# This file is part of the beat.backend.python module of the BEAT platform.   #
-#                                                                             #
-# Commercial License Usage                                                    #
-# Licensees holding valid commercial BEAT licenses may use this file in       #
-# accordance with the terms contained in a written agreement between you      #
-# and Idiap. For further information contact tto@idiap.ch                     #
-#                                                                             #
-# Alternatively, this file may be used under the terms of the GNU Affero      #
-# Public License version 3 as published by the Free Software and appearing    #
-# in the file LICENSE.AGPL included in the packaging of this file.            #
-# The BEAT platform is distributed in the hope that it will be useful, but    #
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
-# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
-#                                                                             #
-# You should have received a copy of the GNU Affero Public License along      #
-# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
-#                                                                             #
-###############################################################################
-
-
-import six
-import numpy
-import nose.tools
-
-from .mocks import MockDataSource, MockDataSink
-
-from ..inputs import Input, InputGroup, InputList
-from ..outputs import Output, OutputList, SynchronizationListener
-from ..dataformat import DataFormat
-from ..algorithm import Algorithm
-
-from . import prefix
-
-
-#----------------------------------------------------------
-
-
-def test_one_full_stream__no_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 6)), [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)])
-    group.add(Input('in_data', dataformat, data_source1))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/one_stream__no_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 6)
-
-    for i in six.moves.range(0, 6):
-        nose.tools.eq_(data_sink1.written[i].start, i)        # 0, 1, 2, 3, 4, 5
-        nose.tools.eq_(data_sink1.written[i].end, i)          # 0, 1, 2, 3, 4, 5
-        nose.tools.eq_(data_sink1.written[i].data.value, i)   # 0, 1, 2, 3, 4, 5
-
-
-#----------------------------------------------------------
-
-
-def test_one_delayed_stream__no_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 3)), [(0, 1), (2, 3), (4, 5)])
-    group.add(Input('in_data', dataformat, data_source1))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/one_stream__no_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 3)
-
-    for i in six.moves.range(0, 3):
-        nose.tools.eq_(data_sink1.written[i].start, i * 2)    # 0, 2, 4
-        nose.tools.eq_(data_sink1.written[i].end, i * 2 + 1)  # 1, 3, 5
-        nose.tools.eq_(data_sink1.written[i].data.value, i)   # 0, 1, 2
-
-
-#----------------------------------------------------------
-
-
-def test_one_full_stream__with_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 6)), [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)])
-    group.add(Input('in_data', dataformat, data_source1))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/one_stream__with_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 3)
-
-    for i in six.moves.range(0, 3):
-        nose.tools.eq_(data_sink1.written[i].start, i * 2)            # 0, 2, 4
-        nose.tools.eq_(data_sink1.written[i].end, i * 2 + 1)          # 1, 3, 5
-        nose.tools.eq_(data_sink1.written[i].data.value, i * 2 + 1)   # 1, 3, 5
-
-
-#----------------------------------------------------------
-
-
-def test_one_delayed_stream__with_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 6)), [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11)])
-    group.add(Input('in_data', 'single_integer', data_source1))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/one_stream__with_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 3)
-
-    for i in six.moves.range(0, 3):
-        nose.tools.eq_(data_sink1.written[i].start, i * 4)            # 0, 4, 8
-        nose.tools.eq_(data_sink1.written[i].end, i * 4 + 3)          # 3, 7, 11
-        nose.tools.eq_(data_sink1.written[i].data.value, i * 2 + 1)   # 1, 3, 5
-
-
-#----------------------------------------------------------
-
-
-def test_two_aligned_streams__no_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 6)), [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)])
-    group.add(Input('in_data1', 'single_integer', data_source1))
-
-    data_source2 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x * 10)), six.moves.range(0, 3)), [(0, 1), (2, 3), (4, 5)])
-    group.add(Input('in_data2', 'single_integer', data_source2))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/two_streams__no_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 6)
-
-    values = [0, 1, 12, 13, 24, 25]
-
-    for i in six.moves.range(0, 6):
-        nose.tools.eq_(data_sink1.written[i].start, i)        # 0, 1, 2, 3, 4, 5
-        nose.tools.eq_(data_sink1.written[i].end, i)          # 0, 1, 2, 3, 4, 5
-        nose.tools.eq_(data_sink1.written[i].data.value, values[i])
-
-
-#----------------------------------------------------------
-
-
-def test_two_aligned_streams__with_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 6)), [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)])
-    group.add(Input('in_data1', 'single_integer', data_source1))
-
-    data_source2 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x * 10)), six.moves.range(0, 3)), [(0, 1), (2, 3), (4, 5)])
-    group.add(Input('in_data2', 'single_integer', data_source2))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/two_streams__with_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 3)
-
-    values = [1, 13, 25]
-
-    for i in six.moves.range(0, 3):
-        nose.tools.eq_(data_sink1.written[i].start, i * 2)        # 0, 2, 4
-        nose.tools.eq_(data_sink1.written[i].end, i * 2 + 1)      # 1, 3, 5
-        nose.tools.eq_(data_sink1.written[i].data.value, values[i])
-
-
-#----------------------------------------------------------
-
-
-def test_two_unaligned_streams__no_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 6)), [(0, 2), (3, 5), (6, 8), (9, 11), (12, 14), (15, 17)])
-    group.add(Input('in_data1', 'single_integer', data_source1))
-
-    data_source2 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x * 10)), six.moves.range(0, 4)), [(0, 4), (5, 9), (10, 13), (14, 17)])
-    group.add(Input('in_data2', 'single_integer', data_source2))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/two_streams__no_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 9)
-
-    starts = [0, 3, 5, 6, 9, 10, 12, 14, 15]
-    ends   = [2, 4, 5, 8, 9, 11, 13, 14, 17]
-    values = [0, 1, 11, 12, 13, 23, 24, 34, 35]
-
-    for i in six.moves.range(0, 9):
-        nose.tools.eq_(data_sink1.written[i].start, starts[i])
-        nose.tools.eq_(data_sink1.written[i].end, ends[i])
-        nose.tools.eq_(data_sink1.written[i].data.value, values[i])
-
-
-#----------------------------------------------------------
-
-
-def test_two_unaligned_streams__with_delay():
-
-    dataformat = DataFormat(prefix, 'user/single_integer/1')
-    assert dataformat.valid
-
-    synchronization_listener = SynchronizationListener()
-
-    # Inputs
-    inputs = InputList()
-
-    group = InputGroup('channel1', synchronization_listener)
-    inputs.add(group)
-
-    data_source1 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x)), six.moves.range(0, 7)), [(0, 2), (3, 5), (6, 8), (9, 11), (12, 14), (15, 17), (18, 20)])
-    group.add(Input('in_data1', 'single_integer', data_source1))
-
-    data_source2 = MockDataSource(map(lambda x: dataformat.type(value=numpy.int32(x * 10)), six.moves.range(0, 4)), [(0, 4), (5, 9), (10, 13), (14, 20)])
-    group.add(Input('in_data2', 'single_integer', data_source2))
-
-    # Outputs
-    outputs = OutputList()
-
-    data_sink1 = MockDataSink(dataformat)
-    outputs.add(Output('out_data', data_sink1, synchronization_listener))
-
-    # Algorithm
-    algorithm = Algorithm(prefix, 'legacy/two_streams__with_delay/1')
-    assert algorithm.valid, algorithm.errors
-    runner = algorithm.runner()
-    assert runner.ready
-
-    while group.hasMoreData():
-        group.restricted_access = False
-        group.next()
-        group.restricted_access = True
-        runner.process(inputs, outputs)
-
-    nose.tools.eq_(len(data_sink1.written), 5)
-
-    starts = [0, 5, 9, 12, 15]
-    ends   = [4, 8, 11, 14, 20]
-    values = [1, 12, 23, 34, 36]
-
-    for i in six.moves.range(0, 5):
-        nose.tools.eq_(data_sink1.written[i].start, starts[i])
-        nose.tools.eq_(data_sink1.written[i].end, ends[i])
-        nose.tools.eq_(data_sink1.written[i].data.value, values[i])
-- 
GitLab