Skip to content

Commit

Permalink
Merge branch 'develop' into 'master'
Browse files Browse the repository at this point in the history
Unit tests

See merge request ropod/execution-monitoring/black-box!17
  • Loading branch information
alex-mitrevski committed Aug 17, 2019
2 parents 220c305 + 1ea8632 commit ab4739d
Show file tree
Hide file tree
Showing 23 changed files with 381 additions and 46 deletions.
12 changes: 12 additions & 0 deletions .gitlab-ci.yml
@@ -0,0 +1,12 @@
image: "git.ropod.org:4567/ropod/execution-monitoring/black-box:mongo"
before_script:
- cat pybb/requirements.txt | xargs -n 1 -L 1 pip3 install
- cd pybb && python3 setup.py install && cd ..
- source /opt/ros/kinetic/setup.bash
- mkdir -p /data/db
- service mongodb start
variables:
DB_HOST: localhost
test:
script:
- python3 -m unittest discover -s 'pybb/test' -p 'test_*.py'
4 changes: 2 additions & 2 deletions config/test_sources.yaml
Expand Up @@ -4,9 +4,9 @@
max_database_size: 102400
# whether or not to create a new DB after max size is reached
split_database: false
db_name: 'log_test'
db_name: 'bb_test_data'
- zyre:
name: black_box_001
name: black_box_101
groups:
- ROPOD
message_types:
Expand Down
File renamed without changes.
Expand Up @@ -10,8 +10,8 @@
import rospy
from termcolor import colored

from rostopic_publisher import RosTopicPublisher
from zyre_publisher import ZyrePublisher
from black_box.automatic_tests.rostopic_publisher import RosTopicPublisher
from black_box.automatic_tests.zyre_publisher import ZyrePublisher

from black_box.config.config_file_reader import ConfigFileReader
from black_box.config.config_utils import ConfigUtils
Expand Down Expand Up @@ -80,14 +80,15 @@ def is_bb_running():
"""
processes = list(psutil.process_iter())
bb_processes = [process for process in processes if 'logger_main.py' in process.cmdline()]
bb_processes = [process for process in processes for argument in process.cmdline() if 'logger_main.py' in argument]
return len(bb_processes) > 0

def check_logs(config_params, test_duration):
def check_logs(config_params, test_duration, print_output=False):
"""Check the logs in mongodb and print the status.
:config_params: black_box.config.ConfigParams
:test_duration: float
:print_output: bool
:returns: None
"""
Expand Down Expand Up @@ -130,38 +131,49 @@ def check_logs(config_params, test_duration):
'collection': topic_name,
'expected_size': expected_size,
'collection_size': collection_size})
if not fail:
if not fail and print_output:
print(colored("All topics have their respective collection in mongoDB", "green"))
for comparison in size_status:
color = "green" if comparison['expected_size'] == comparison['collection_size'] else "red"
string = comparison['collection'] + ': ' + str(comparison['collection_size']) \
+ '/' + str(comparison['expected_size'])
print(colored(string, color))
if print_output:
print(colored(string, color))
return size_status

if __name__ == "__main__":
if len(sys.argv) < 2:
print('Usage: python3 automatic_tester.py [absolute-path-to-black-box-config-file]')
sys.exit(1)
BB_CONFIG_FILE = sys.argv[1]

TEST_DURATION = 20 #seconds
def main(config_file, test_duration, print_output=False):
""" main function which uses AutomaticTester class and tests if bb works or not
:config_file: string (file path of the config file to be used)
:test_duration: float/int (time for which the publishers should be on)
"""
# only proceed if black box is running
if not is_bb_running():
print('Blackbox is not running. Please make sure it is running before',
'executing this test script.')
sys.exit(1)

CONFIG_PARAMS = ConfigFileReader.load_config(BB_CONFIG_FILE)
DBUtils.clear_db(CONFIG_PARAMS.default.db_name)
config_params = ConfigFileReader.load_config(config_file)
DBUtils.clear_db(config_params.default.db_name)

TESTER = AutomaticTester(CONFIG_PARAMS, TEST_DURATION)
tester = AutomaticTester(config_params, test_duration)
print("initialised all publisher")

TESTER.start()
tester.start()
print("publishers running")

TESTER.stop()
tester.stop()
print("publishers stopped")

check_logs(CONFIG_PARAMS, TEST_DURATION)
return check_logs(config_params, test_duration, print_output=print_output)

if __name__ == "__main__":
if len(sys.argv) < 2:
print('Usage: python3 automatic_tester.py [absolute-path-to-black-box-config-file]')
sys.exit(1)
BB_CONFIG_FILE = sys.argv[1]

TEST_DURATION = 20 #seconds

OUTPUT = main(BB_CONFIG_FILE, TEST_DURATION, print_output=True)
print(OUTPUT)
Expand Up @@ -21,8 +21,10 @@ class ZyrePublisher(RopodPyre):
"""

def __init__(self, msg_type, groups, **kwargs):
super(ZyrePublisher, self).__init__(
'zyre_publisher_automatic_test_'+msg_type, groups, list(),
super(ZyrePublisher, self).__init__({
'node_name': 'zyre_publisher_automatic_test_'+msg_type,
'groups': groups,
'message_types': list()},
verbose=False, acknowledge=False)
self.msg_type = msg_type
self.num_of_msgs = kwargs.get('num_of_msgs', 10)
Expand All @@ -41,9 +43,7 @@ def start_publishing(self):
self._send_request(self.msg_type)
time.sleep(self.sleep_time)
self.publishing = False
print('before shutdown')
self.shutdown()
print('after shutdown')

def _send_request(self, msg_type, payload_dict=None):
request_msg = dict()
Expand Down
2 changes: 1 addition & 1 deletion pybb/black_box/config/config_file_reader.py
Expand Up @@ -9,7 +9,7 @@ def load_config(config_file_name):
config_data = dict()
try:
with open(config_file_name, 'r') as config_file:
config_data = yaml.load(config_file)
config_data = yaml.safe_load(config_file)
except Exception as exc:
print('[config_file_reader] An error occured while reading {0}'.format(config_file_name))
print(exc)
Expand Down
6 changes: 3 additions & 3 deletions pybb/black_box/datalogger/data_readers/zyre_reader.py
Expand Up @@ -15,9 +15,9 @@ class ZyreReader(RopodPyre):
'''
def __init__(self, config_params, data_logger, interface_name='zyre'):
super(ZyreReader, self).__init__(node_name=config_params.node_name,
groups=config_params.groups,
message_types=config_params.message_types)
super(ZyreReader, self).__init__({'node_name': config_params.node_name,
'groups': config_params.groups,
'message_types': config_params.message_types})
self.config_params = config_params
self.data_logger = data_logger
self.logging = False
Expand Down
17 changes: 13 additions & 4 deletions pybb/black_box/datalogger/loggers/mongodb_logger.py
@@ -1,6 +1,7 @@
import pymongo as pm
from black_box.datalogger.loggers.logger_base import LoggerBase
from black_box.config.config_utils import ConfigUtils
from black_box_tools.db_utils import DBUtils

class MongoDBLogger(LoggerBase):
def __init__(self, db_name, db_port, split_db, max_db_size):
Expand All @@ -17,7 +18,11 @@ def write_metadata(self, config_params):
'''
if config_params:
client = pm.MongoClient(port=self.db_port)
(host, port) = DBUtils.get_db_host_and_port()
if port != self.db_port:
port = self.db_port

client = pm.MongoClient(host=host, port=port)
db = client[self.db_name]
collection_names = db.list_collection_names()
if 'black_box_metadata' in collection_names:
Expand Down Expand Up @@ -49,8 +54,8 @@ def write_metadata(self, config_params):
metadata['ros']['direct_msg_mapping'] = topic_params.metadata.direct_msg_mapping
collection.insert_one(metadata)
else:
print('[write_metadata] config_params needs to be of type ' + \
'black_box.config.config_params.ConfigParams')
raise AssertionError('[write_metadata] config_params needs to be of type ' + \
'black_box.config.config_params.ConfigParams')

def log_data(self, variable, timestamp, data):
'''Logs the data dictionary for the given variable;
Expand All @@ -61,7 +66,11 @@ def log_data(self, variable, timestamp, data):
@param data -- a dictionary to be logged
'''
client = pm.MongoClient(port=self.db_port)
(host, port) = DBUtils.get_db_host_and_port()
if port != self.db_port:
port = self.db_port

client = pm.MongoClient(host=host, port=port)
db = client[self.db_name]
collection = db[variable]
data['timestamp'] = timestamp
Expand Down
10 changes: 6 additions & 4 deletions pybb/black_box/datalogger/pyre_comm/bb_pyre_comm.py
Expand Up @@ -6,7 +6,7 @@

class BlackBoxPyreCommunicator(RopodPyre):

"""Receive commands from other nodes
"""Receive commands from other nodes
example: starting and stoping of logging action.
:groups: list of string (pyre groups)
Expand All @@ -15,14 +15,16 @@ class BlackBoxPyreCommunicator(RopodPyre):
"""

def __init__(self, groups, black_box_id):
super(BlackBoxPyreCommunicator, self).__init__(
'bb_pyre_comm'+black_box_id, groups, list(), verbose=True)
super(BlackBoxPyreCommunicator, self).__init__({
'node_name': 'bb_pyre_comm'+black_box_id,
'groups': groups,
'message_types': list()})
self.logging = True
self.black_box_id = black_box_id
self.start()

def receive_msg_cb(self, msg):
'''Processes the incoming messages
'''Processes the incoming messages
:msg: string (a message in JSON format)
Expand Down
15 changes: 11 additions & 4 deletions pybb/black_box/query_interface/db_interface.py
Expand Up @@ -22,7 +22,11 @@ def get_variables(self, data_source):
start with the data source name
'''
client = pm.MongoClient(port=self.db_port)
(host, port) = DBUtils.get_db_host_and_port()
if port != self.db_port:
port = self.db_port

client = pm.MongoClient(host=host, port=port)
database = client[self.db_name]
collection_names = database.list_collection_names()

Expand Down Expand Up @@ -58,8 +62,7 @@ def get_data(self, collection_name, variable_names, start_time=-1, end_time=-1):
no upper bound for the timestamp of the retrieved data)
'''
docs = DBUtils.get_doc_cursor(
self.db_name, collection_name, start_time, end_time, self.db_port)
docs = DBUtils.get_doc_cursor(self.db_name, collection_name, start_time, end_time)

var_data = {}
var_full_names = {}
Expand Down Expand Up @@ -89,7 +92,11 @@ def get_latest_data(self, collection_name, variable_names):
@param variable_names -- list of variable names that should be retrieved from the collection
'''
client = pm.MongoClient(port=self.db_port)
(host, port) = DBUtils.get_db_host_and_port()
if port != self.db_port:
port = self.db_port

client = pm.MongoClient(host=host, port=port)
database = client[self.db_name]
collection = database[collection_name]
doc = collection.find_one(sort=[('timestamp', pm.DESCENDING)])
Expand Down
6 changes: 4 additions & 2 deletions pybb/black_box/query_interface/query_interface.py
Expand Up @@ -11,8 +11,10 @@ class BlackBoxQueryInterface(RopodPyre):
'''
def __init__(self, data_sources, black_box_id, groups, db_name='logs', db_port=27017):
super(BlackBoxQueryInterface, self).__init__(black_box_id + '_query_interface',
groups, list())
super(BlackBoxQueryInterface, self).__init__({
'node_name': black_box_id + '_query_interface',
'groups': groups,
'message_types': list()})
self.data_sources = data_sources
self.black_box_id = black_box_id
self.db_interface = DBInterface(db_name, db_port)
Expand Down
2 changes: 1 addition & 1 deletion pybb/logger_main.py
Expand Up @@ -29,7 +29,7 @@
split_db=config_params.default.split_db,
max_db_size=config_params.default.max_db_size)
logger.write_metadata(config_params)

readers = {}
for reader_name in config_params.__dict__.keys():
if reader_name != 'default': # TODO:need better way to ignore default
Expand Down
2 changes: 1 addition & 1 deletion pybb/query_interface_main.py
Expand Up @@ -13,7 +13,7 @@ def __init__(self):
def get_config_params(config_file):
config_data = dict()
with open(config_file, 'r') as bb_config:
config_data = yaml.load(bb_config)
config_data = yaml.safe_load(bb_config)

config_params = ConfigParams()
for data_item in config_data:
Expand Down
7 changes: 6 additions & 1 deletion pybb/requirements.txt
@@ -1,5 +1,10 @@
pymongo
PyYAML
rospkg
catkin_pkg
psutil
termcolor
numpy
scipy
git+https://github.com/ropod-project/rospy_message_converter.git#egg=rospy_message_converter
git+https://github.com/ropod-project/ropod_common.git#egg=ropod&subdirectory=pyropod
git+https://github.com/ropod-project/black-box-tools.git#egg=black_box_tools
Binary file added pybb/test/bb_test_data/black_box_metadata.bson
Binary file not shown.
1 change: 1 addition & 0 deletions pybb/test/bb_test_data/black_box_metadata.metadata.json
@@ -0,0 +1 @@
{ "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "logs.black_box_metadata" } ] }
Binary file added pybb/test/bb_test_data/ros_ropod_cmd_vel.bson
Binary file not shown.
1 change: 1 addition & 0 deletions pybb/test/bb_test_data/ros_ropod_cmd_vel.metadata.json
@@ -0,0 +1 @@
{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"logs.ros_ropod_cmd_vel"}],"uuid":"395b68b92bc340ba94dbc341d855a6a4"}
Binary file not shown.
@@ -0,0 +1 @@
{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"logs.ros_sw_ethercat_parser_data"}],"uuid":"e3ff3e9c3c1e4e378988666d224c199d"}
62 changes: 62 additions & 0 deletions pybb/test/test_bb_logging.py
@@ -0,0 +1,62 @@
#! /usr/bin/env python3
"""This module contains unit tests to evaluate the logging capabilities of black
box using automatic test"""

import time
import os
import signal
import subprocess
import unittest

from black_box.automatic_tests.automatic_tester import main as automatic_tester_main

class TestBBLogging(unittest.TestCase):

""" Unit Tests for black box using automated tests"""

@classmethod
def setUpClass(cls):
cls.threshold = 0.9 # percent of message that are required to be logged

test_dir = os.path.abspath(os.path.dirname(__file__))
pybb_dir = os.path.dirname(test_dir)
main_dir = os.path.dirname(pybb_dir)
bb_program_file = os.path.join(pybb_dir, 'logger_main.py')
cls.config_file_path = os.path.join(main_dir, 'config/test_sources.yaml')

bb_output_file_path = '/tmp/bb_output_file_' + str(time.time()).replace('.', '_')
cls.bb_output_file = open(bb_output_file_path, 'w')

print("Starting roscore as a process")
commands = ['roscore']
cls.roscore_process = subprocess.Popen(commands, stdout=cls.bb_output_file)

print("Starting black box as a process")
commands = [str(bb_program_file), str(cls.config_file_path)]
cls.bb_process = subprocess.Popen(commands, stdout=cls.bb_output_file)
print("Waiting for 10s for black box process to initialise completely...")
time.sleep(10)

@classmethod
def tearDownClass(cls):
cls.roscore_process.send_signal(signal.SIGINT)
cls.bb_process.send_signal(signal.SIGINT)
cls.bb_output_file.close()

def test_bb_with_test_config(self):
"""Test black box with test_sources.yaml config file"""

print("Starting test...")
output = automatic_tester_main(self.config_file_path, 10)
for topic in output:
string = topic['collection'] + ': ' + str(topic['collection_size']) \
+ '/' + str(topic['expected_size'])
print(string)
self.assertGreaterEqual(
float(topic['collection_size'])/topic['expected_size'],
self.threshold)
print("Ending test")


if __name__ == '__main__':
unittest.main()

0 comments on commit ab4739d

Please sign in to comment.