Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions riotctrl/ctrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Define class to abstract a node over the RIOT build system.
"""

import abc
import os
import time
import logging
Expand Down Expand Up @@ -188,3 +189,67 @@ def make_command(self, targets):
command.extend(dir_cmd)
command.extend(targets)
return command


class RIOTCtrlFactoryBase(abc.ABC):
# pylint: disable=too-few-public-methods
# A factory usually does not have more methods than one.
"""Abstract factory to create different RIOTCtrl."""

@abc.abstractmethod
def get_ctrl(self, application_directory='.', env=None):
"""
Returns a RIOTCtrl object of a class specified by the Factory

:param application_directory: `application_directory` initialization
parameter for the RIOTCtrl object
:param env: `env` initialization parameter for
the RIOTCtrl object.
"""
raise NotImplementedError


class RIOTCtrlBoardFactory(RIOTCtrlFactoryBase):
# pylint: disable=too-few-public-methods
# A factory usually does not have more methods than one.
"""Factory mixin to create different RIOTCtrl types based on
the BOARD environment variable.

:param board_cls: A dict that maps the `BOARD` environment variable to a
RIOTCtrl class.
"""
DEFAULT_CLS = RIOTCtrl
BOARD_CLS = {}

def __init__(self, board_cls=None):
self.board_cls = {}
self.board_cls.update(self.BOARD_CLS)
if board_cls is not None:
self.board_cls.update(board_cls)

def get_ctrl(self, application_directory='.', env=None):
"""
Returns a RIOTCtrl object of a class as specified in `board_cls` on
initialization.

:param application_directory: `application_directory` initialization
parameter for the RIOTCtrl object
:param env: `env` initialization parameter for
the RIOTCtrl object. This will also be
used to determine the actual class of
the return value.

When `BOARD` is set in the environment variables when `env` is provided
in `env`, that value is used to look-up the RIOTCtrl class in the
factory's `board_cls` for that specific `BOARD` value.
"""
the_env = {}
the_env.update(os.environ)
if env:
the_env.update(env)
if 'BOARD' not in the_env or the_env['BOARD'] not in self.board_cls:
cls = self.DEFAULT_CLS
else:
cls = self.board_cls[the_env['BOARD']]
# cls does its own fetching of `os.environ` so only provide `env` here
return cls(application_directory=application_directory, env=env)
61 changes: 61 additions & 0 deletions riotctrl/tests/ctrl_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,64 @@ def test_term_cleanup(app_pidfile_env):
# File should not exist anymore so no error to create one
# File must exist to be cleaned by tempfile
open(tmpfile.name, 'x')


class CtrlMock1(riotctrl.ctrl.RIOTCtrl):
"""Mock to test RIOTCtrlFactoryBase descendents"""


class CtrlMock2(riotctrl.ctrl.RIOTCtrl):
"""Another mock to test RIOTCtrlFactoryBase descendents"""


def test_board_factory_wo_board():
"""Tests if riotctrl.ctrl.RIOTCtrlBoardFactory defaults to
riotctrl.ctrl.RIOTCtrl if no mapping exists for a device"""
factory = riotctrl.ctrl.RIOTCtrlBoardFactory()
assert factory.DEFAULT_CLS is riotctrl.ctrl.RIOTCtrl
ctrl = factory.get_ctrl()
# pylint: disable=unidiomatic-typecheck
# in this case we want to know the exact type
assert type(ctrl) is riotctrl.ctrl.RIOTCtrl


def test_w_board_in_default_board_cls():
"""Tests if riotctrl.ctrl.RIOTCtrlBoardFactory returns a class in the
static mapping of the factory exists"""
env = {'BOARD': 'mock'}
riotctrl.ctrl.RIOTCtrlBoardFactory.BOARD_CLS = {'mock': CtrlMock1}
factory = riotctrl.ctrl.RIOTCtrlBoardFactory()
assert 'mock' in factory.board_cls
ctrl = factory.get_ctrl(env=env)
# pylint: disable=unidiomatic-typecheck
# in this case we want to know the exact type
assert type(ctrl) is CtrlMock1


def test_w_board_in_not_default_board_cls():
"""Tests if riotctrl.ctrl.RIOTCtrlBoardFactory defaults to
riotctrl.ctrl.RIOTCtrl if no mapping exists for a device with an existing
mapping"""
env = {'BOARD': 'foobar'}
riotctrl.ctrl.RIOTCtrlBoardFactory.BOARD_CLS = {'mock': CtrlMock1}
factory = riotctrl.ctrl.RIOTCtrlBoardFactory()
assert 'mock' in factory.board_cls
ctrl = factory.get_ctrl(env=env)
# pylint: disable=unidiomatic-typecheck
# in this case we want to know the exact type
assert type(ctrl) is riotctrl.ctrl.RIOTCtrl


def test_w_board_custom_board_cls():
"""Tests if riotctrl.ctrl.RIOTCtrlBoardFactory returns a class in the
dynamic mapping of the factory exists"""
env = {'BOARD': 'mock'}
riotctrl.ctrl.RIOTCtrlBoardFactory.BOARD_CLS = {'mock': CtrlMock1}
factory = riotctrl.ctrl.RIOTCtrlBoardFactory(
board_cls={'mock': CtrlMock2}
)
assert 'mock' in factory.board_cls
ctrl = factory.get_ctrl(env=env)
# pylint: disable=unidiomatic-typecheck
# in this case we want to know the exact type
assert type(ctrl) is CtrlMock2