Skip to content
This repository has been archived by the owner on Jan 14, 2024. It is now read-only.

Commit

Permalink
Increased coverage of tasks parsing in YAML parser
Browse files Browse the repository at this point in the history
  • Loading branch information
blackandred committed May 8, 2020
1 parent 5a58b32 commit 6013731
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 14 deletions.
4 changes: 4 additions & 0 deletions src/rkd/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,7 @@ def rkd(self, args: list) -> str:
def is_silent_in_observer(self) -> bool:
""" Internally used property """
return False

def io(self):
return self._io

34 changes: 27 additions & 7 deletions src/rkd/inputoutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class IO:
log_level = LEVEL_INFO

@contextmanager
def capture_descriptors(self, target_file: str = None):
def capture_descriptors(self, target_file: str = None, stream = None):
""" Capture stdout and stderr per task """

if this.IS_CAPTURING_DESCRIPTORS:
Expand All @@ -57,13 +57,20 @@ def capture_descriptors(self, target_file: str = None):
sys_stderr = sys.stderr
log_file = None

outputs_stdout = [sys_stdout]
outputs_stderr = [sys_stderr]

if target_file:
log_file = open(target_file, 'wb')
sys.stdout = StandardOutputReplication([sys_stdout, log_file])
sys.stderr = StandardOutputReplication([sys_stderr, log_file])
else:
sys.stdout = StandardOutputReplication([sys_stdout])
sys.stderr = StandardOutputReplication([sys_stderr])
outputs_stdout.append(log_file)
outputs_stderr.append(log_file)

if stream:
outputs_stdout.append(stream)
outputs_stderr.append(stream)

sys.stdout = StandardOutputReplication([sys_stdout])
sys.stderr = StandardOutputReplication([sys_stderr])

yield
sys.stdout = sys_stdout
Expand Down Expand Up @@ -191,7 +198,7 @@ def info_msg(self, text):
class SystemIO(IO):
""" Used for logging outside of tasks """

def capture_descriptors(self, target_file: str = None):
def capture_descriptors(self, target_file: str = None, stream = None):
pass


Expand All @@ -201,3 +208,16 @@ def _stdout(self, text):

def _stderr(self, text):
pass


class BufferedSystemIO(SystemIO):
_buffer = ''

def _stdout(self, text):
self._buffer += text

def _stderr(self, text):
self._buffer += text

def get_value(self):
return self._buffer
12 changes: 6 additions & 6 deletions src/rkd/yaml_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import importlib
from argparse import ArgumentParser
from typing import List, Tuple, Union, Callable
from traceback import print_exc
from traceback import format_exc
from .exception import YamlParsingException
from .inputoutput import SystemIO
from .inputoutput import IO
from .syntax import TaskDeclaration, TaskAliasDeclaration
from .standardlib import CallableTask
from .contract import ExecutionContext, TaskInterface
Expand All @@ -18,9 +18,9 @@ class YamlParser:
Translates YAML syntax into Python syntax of makefile (makefile.yaml -> makefile.py)
"""

io: SystemIO
io: IO

def __init__(self, io: SystemIO):
def __init__(self, io: IO):
self.io = io

def parse(self, content: str, rkd_path: str) -> Tuple[List[TaskDeclaration], List[TaskAliasDeclaration]]:
Expand Down Expand Up @@ -137,10 +137,10 @@ def execute(ctx: ExecutionContext, this: TaskInterface) -> bool:
return eval(compile(eval_expr, filename, 'eval'))

except Exception as e:
this._io.error_msg('Error while executing step %i in task "%s". Exception: %s' % (
this.io().error_msg('Error while executing step %i in task "%s". Exception: %s' % (
step_num, task_name, str(e)
))
print_exc()
this.io().error_msg(format_exc())

return False

Expand Down
70 changes: 69 additions & 1 deletion test/test_yaml_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import unittest
from rkd.yaml_context import YamlParser
from rkd.inputoutput import NullSystemIO
from rkd.inputoutput import IO, NullSystemIO, BufferedSystemIO
from rkd.exception import DeclarationException, YamlParsingException
from rkd.contract import ExecutionContext


class TestYamlContext(unittest.TestCase):
Expand All @@ -26,3 +27,70 @@ def test():
factory.parse_imports(['rkd.standardlib.python.WRONG_NAME'])

self.assertRaises(YamlParsingException, test)

def test_parse_tasks_successful_case(self):
"""
Successful case with description, arguments and bash steps
"""

input_tasks = {
':resistentia': {
'description': 'Against moving the costs of the crisis to the workers!',
'arguments': {
'--picket': {
'help': 'Picket form',
'required': False,
'action': 'store_true'
}
},
'steps': [
'rm -f /tmp/.test_parse_tasks && echo "Resistentia!" > /tmp/.test_parse_tasks'
]
}
}

io = IO()
factory = YamlParser(io)
parsed_tasks = factory.parse_tasks(input_tasks, '')

self.assertEqual(':resistentia', parsed_tasks[0].to_full_name(),
msg='Expected that the task name will be present')

declaration = parsed_tasks[0]
declaration.get_task_to_execute().execute(ExecutionContext(declaration))

with open('/tmp/.test_parse_tasks', 'r') as test_helper:
self.assertIn('Resistentia!', test_helper.read(),
msg='Expected that echo contents will be visible')

def test_parse_tasks_signals_error_instead_of_throwing_exception(self):
"""
Test that error thrown by executed Python code will
"""

input_tasks = {
':song': {
'description': 'Bella Ciao is an Italian protest folk song that originated in the hardships of the ' +
'mondina women, the paddy field workers in the late 19th century who sang it to ' +
'protest against harsh working conditions in the paddy fields of North Italy',
'steps': [
'''#!python
print(syntax-error-here)
'''
]
}
}

io = BufferedSystemIO()
factory = YamlParser(io)
parsed_tasks = factory.parse_tasks(input_tasks, '')

declaration = parsed_tasks[0]
task = declaration.get_task_to_execute()
task._io = io

result = task.execute(ExecutionContext(declaration))

self.assertEqual(False, result, msg='Expected that syntax error would result in a failure')
self.assertIn("NameError: name 'syntax' is not defined", io.get_value(), msg='Error message should be attached')
self.assertIn('File ":song@step 1", line 1', io.get_value(), msg='Stacktrace should be attached')

0 comments on commit 6013731

Please sign in to comment.