Skip to content

Commit

Permalink
Finalizing redis_adapter
Browse files Browse the repository at this point in the history
AdapterDataSource
  Removed print statements
  Overrode has_data to return instance value of new_data
test_redis_adapter
  Tested previously untested code
  Adjusted tests for changes to AdapterDataSource
  • Loading branch information
asgibson committed Aug 17, 2023
1 parent 88c1445 commit f8e5c97
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 31 deletions.
9 changes: 4 additions & 5 deletions onair/src/run_scripts/redis_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,14 @@ def subscribe_message(self, channel):
listen_thread = threading.Thread(target=self.message_listener)
listen_thread.start()


def get_next(self):
"""Provides the latest data from REDIS channel"""
data_available = False

while not data_available:
with self.new_data_lock:
data_available = self.new_data

data_available = self.has_data()
if not data_available:
time.sleep(0.01)

Expand All @@ -67,7 +66,6 @@ def get_next(self):
self.double_buffer_read_index = (self.double_buffer_read_index + 1) % 2
read_index = self.double_buffer_read_index

print("Reading buffer: {}".format(read_index))
return self.currentData[read_index]['data']

def has_more(self):
Expand All @@ -77,7 +75,6 @@ def has_more(self):
def message_listener(self):
"""Loop for listening for messages on channel"""
for message in self.pubsub.listen():
print("Received from REDIS: ", message)
if message['type'] == 'message':
data = json.loads(message['data'])

Expand All @@ -88,3 +85,5 @@ def message_listener(self):
with self.new_data_lock:
self.new_data = True

def has_data(self):
return self.new_data
139 changes: 113 additions & 26 deletions test/src/run_scripts/test_redis_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from mock import MagicMock, PropertyMock
import onair.src.run_scripts.redis_adapter as redis_adapter
from onair.src.run_scripts.redis_adapter import AdapterDataSource
from importlib import reload
import redis
import threading

Expand Down Expand Up @@ -115,87 +114,106 @@ def test_redis_adapter_AdapterDataSource_subscribe_message_does_nothing_on_False

# get_next tests

def test_redis_adapter_AdapterDataSource_get_next_when_new_data_is_true():
def test_redis_adapter_AdapterDataSource_get_next_returns_expected_data_when_new_data_is_true_and_double_buffer_read_index_is_0():
# Arrange
# Renew AdapterDataSource to ensure test independence
cut = AdapterDataSource.__new__(AdapterDataSource)
cut.new_data = True
cut.new_data_lock = MagicMock()
cut.double_buffer_read_index = pytest.gen.randint(0,1)
cut.double_buffer_read_index = 0
pre_call_index = cut.double_buffer_read_index
expected_result = MagicMock()
cut.currentData = []
if pre_call_index == 0:
cut.currentData.append({'data': MagicMock()})
cut.currentData.append({'data': expected_result})
else:
cut.currentData.append({'data': MagicMock()})
cut.currentData.append({'data': expected_result})
cut.currentData.append({'data': MagicMock()})
cut.currentData.append({'data': expected_result})

# Act
result = cut.get_next()

# Assert
assert cut.new_data == False
if pre_call_index == 0:
assert cut.double_buffer_read_index == 1
elif pre_call_index == 1:
assert cut.double_buffer_read_index == 0
else:
assert False
assert cut.double_buffer_read_index == 1
assert result == expected_result

def test_redis_adapter_AdapterDataSource_get_next_returns_expected_data_when_new_data_is_true_and_double_buffer_read_index_is_1():
# Arrange
# Renew AdapterDataSource to ensure test independence
cut = AdapterDataSource.__new__(AdapterDataSource)
cut.new_data = True
cut.new_data_lock = MagicMock()
cut.double_buffer_read_index = 1
pre_call_index = cut.double_buffer_read_index
expected_result = MagicMock()
cut.currentData = []
cut.currentData.append({'data': expected_result})
cut.currentData.append({'data': MagicMock()})

# Act
result = cut.get_next()

# Assert
assert cut.new_data == False
assert cut.double_buffer_read_index == 0
assert result == expected_result

def test_redis_adapter_AdapterDataSource_get_next_when_called_multiple_times_when_new_data_is_true():
# Arrange
# Renew AdapterDataSource to ensure test independence
cut = AdapterDataSource.__new__(AdapterDataSource)
cut.double_buffer_read_index = pytest.gen.randint(0,1)
cut.new_data_lock = MagicMock()
cut.currentData = [MagicMock(), MagicMock()]
pre_call_index = cut.double_buffer_read_index
expected_data = []

# Act
results = []
num_calls = pytest.gen.randint(2,10) # arbitrary, 2 to 10
for i in range(num_calls):
cut.new_data = True
fake_new_data = MagicMock()
if cut.double_buffer_read_index == 0:
cut.currentData[1] = {'data': fake_new_data}
else:
cut.currentData[0] = {'data': fake_new_data}
expected_data.append(fake_new_data)
results.append(cut.get_next())

# Assert
assert cut.new_data == False
for i in range(num_calls):
results[i] = cut.currentData[pre_call_index]['data']
pre_call_index = (pre_call_index + 1) % 2
assert cut.double_buffer_read_index == pre_call_index
results[i] = expected_data[i]
assert cut.double_buffer_read_index == (num_calls + pre_call_index) % 2

def test_redis_adapter_AdapterDataSource_get_next_behavior_when_new_data_is_false_then_true(mocker):
def test_redis_adapter_AdapterDataSource_get_next_waits_until_data_is_available(mocker):
# Arrange
# Renew AdapterDataSource to ensure test independence
cut = AdapterDataSource.__new__(AdapterDataSource)
cut.new_data_lock = MagicMock()
cut.double_buffer_read_index = pytest.gen.randint(0,1)
pre_call_index = cut.double_buffer_read_index
expected_result = MagicMock()
cut.new_data = None
cut.currentData = []
if pre_call_index == 0:
cut.currentData.append({'data': MagicMock()})
cut.currentData.append({'data': expected_result})
else:
cut.currentData.append({'data': MagicMock()})
cut.currentData.append({'data': expected_result})
cut.currentData.append({'data': MagicMock()})

num_falses = pytest.gen.randint(1, 10)
side_effect_list = [False] * num_falses
side_effect_list.append(True)

print(side_effect_list)
cut.new_data = PropertyMock()
cut.new_data.side_effect=side_effect_list
mocker.patch.object(cut, 'has_data', side_effect=side_effect_list)
mocker.patch('onair.src.run_scripts.redis_adapter.time.sleep')

# Act
result = cut.get_next()

# Assert
assert cut.has_data.call_count == num_falses + 1
assert redis_adapter.time.sleep.call_count == num_falses
assert cut.new_data == False
if pre_call_index == 0:
Expand All @@ -207,8 +225,77 @@ def test_redis_adapter_AdapterDataSource_get_next_behavior_when_new_data_is_fals

assert result == expected_result


# has_more tests
def test_redis_adapter_AdapterDataSource_has_more_returns_True():
def test_redis_adapter_AdapterDataSource_has_more_always_returns_True():
cut = AdapterDataSource.__new__(AdapterDataSource)
assert cut.has_more() == True

# message_listener tests
def test_redis_adapter_AdapterDataSource_message_listener_does_not_load_json_when_receive_type_is_not_message(mocker):
# Arrange
cut = AdapterDataSource.__new__(AdapterDataSource)
ignored_message_types = ['subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe', 'pmessage']
fake_message = {}
fake_message['type'] = pytest.gen.choice(ignored_message_types)

cut.pubsub = MagicMock()
mocker.patch.object(cut.pubsub, 'listen', return_value=[fake_message])
mocker.patch('onair.src.run_scripts.redis_adapter.json.loads')

# Act
cut.message_listener()

# Assert
assert redis_adapter.json.loads.call_count == 0

def test_redis_adapter_AdapterDataSource_message_listener_loads_message_info_when_receive_type_is_message(mocker):
# Arrange
cut = AdapterDataSource.__new__(AdapterDataSource)
assert cut.has_more
cut.new_data_lock = MagicMock()
cut.new_data = None
cut.double_buffer_read_index = pytest.gen.randint(0,1)
cut.currentData = [{}, {}]
cut.pubsub = MagicMock()

fake_message = {}
fake_message_data = {}
fake_message['type'] = 'message'
fake_message['data'] = fake_message_data
fake_data = {}

expected_index = (cut.double_buffer_read_index + 1) % 2
expected_data_headers = []
expected_data_values = []

num_fake_data = pytest.gen.randint(1,10)
for i in range(num_fake_data):
fake_data_header = str(MagicMock())
fake_data_value = MagicMock()
fake_data[fake_data_header] = fake_data_value
expected_data_headers.append(fake_data_header)
expected_data_values.append(fake_data_value)
mocker.patch.object(cut.pubsub, 'listen', return_value=[fake_message])
mocker.patch('onair.src.run_scripts.redis_adapter.json.loads', return_value=fake_data)

# Act
cut.message_listener()

# Assert
assert redis_adapter.json.loads.call_count == 1
assert redis_adapter.json.loads.call_args_list[0].args == (fake_message_data,)
assert cut.currentData[expected_index]['headers'] == expected_data_headers
assert cut.currentData[expected_index]['data'] == expected_data_values
assert cut.new_data == True

# has_data tests
def test_redis_adapter_AdapterDataSource_has_data_returns_instance_new_data():
cut = AdapterDataSource.__new__(AdapterDataSource)
expected_result = MagicMock()
cut.new_data = expected_result

result = cut.has_data()

assert result == expected_result



0 comments on commit f8e5c97

Please sign in to comment.