Skip to content

Commit

Permalink
Merge pull request #12 from DigitalPhonetics/unit_tests
Browse files Browse the repository at this point in the history
Unit tests
  • Loading branch information
lvanderlyn authored Jul 20, 2020
2 parents a67478b + 5b01036 commit 13d74be
Show file tree
Hide file tree
Showing 23 changed files with 5,386 additions and 7 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ wheels/
*.egg
MANIFEST
adviser_env/
adviserenv/

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down Expand Up @@ -198,4 +199,7 @@ tools/opensmile-2.3.0/

adviser/tools/opencv*
adviser/tools/OpenFace
adviser/tools/cppzmq
adviser/tools/cppzmq*
adviser/tools/dlib*
adviser/tools/zeromq*
adviser/tools/*.zip
22 changes: 17 additions & 5 deletions adviser/services/policy/policy_handcrafted.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def dialog_start(self):
self.s_index = 0 # the index in current suggestions for the current system reccomendation

@PublishSubscribe(sub_topics=["beliefstate"], pub_topics=["sys_act", "sys_state"])
def choose_sys_act(self, beliefstate: BeliefState = None) \
def choose_sys_act(self, beliefstate: BeliefState) \
-> dict(sys_act=SysAct):

"""
Expand Down Expand Up @@ -103,6 +103,10 @@ def choose_sys_act(self, beliefstate: BeliefState = None) \
sys_state["last_act"] = sys_act
return {'sys_act': sys_act, "sys_state": sys_state}

# Handles case where it was the first turn, but there are user acts
elif self.first_turn:
self.first_turn = False

if self.turns >= self.max_turns:
sys_act = SysAct()
sys_act.type = SysActionType.Bye
Expand All @@ -125,10 +129,18 @@ def choose_sys_act(self, beliefstate: BeliefState = None) \
sys_act.type = SysActionType.RequestMore
# If user only says hello, request a random slot to move dialog along
elif UserActionType.Hello in beliefstate["user_acts"] or UserActionType.SelectDomain in beliefstate["user_acts"]:
sys_act = SysAct()
sys_act.type = SysActionType.Request
slot = self._get_open_slot(beliefstate)
sys_act.add_value(slot)
# as long as there are open slots, choose one randomly
if self._get_open_slot(beliefstate):
sys_act = SysAct()
sys_act.type = SysActionType.Request
slot = self._get_open_slot(beliefstate)
sys_act.add_value(slot)

# If there are no more open slots, ask the user if you can help with anything else since
# this can only happen in the case an offer has already been made --LV
else:
sys_act = SysAct()
sys_act.type = SysActionType.RequestMore

# If we switch to the domain, start a new dialog
if UserActionType.SelectDomain in beliefstate["user_acts"]:
Expand Down
2 changes: 1 addition & 1 deletion adviser/services/policy/rl/train_dqnpolicy.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def train(domain_name: str, log_to_file: bool, seed: int, train_epochs: int, tra

if __name__ == "__main__":
# possible test domains
domains = ['courses', 'lecturers']
domains = ['lecturers']

# command line arguments
parser = argparse.ArgumentParser()
Expand Down
235 changes: 235 additions & 0 deletions adviser/tests/bst/bst_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import os
import sys
from copy import deepcopy

def get_root_dir():
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


sys.path.append(get_root_dir())
from services.bst import HandcraftedBST
from utils import UserActionType, UserAct


def test_initialize_bst_without_domain():
"""
Tests whether the initialization of a BST without domain executes without errors.
"""
HandcraftedBST()


def test_reset_beliefstate_on_start(bst):
"""
Tests whether the beliefstate resets on dialog start.
Args:
bst: BST Object (given in conftest.py)
"""
previous_bs = bst.bs
bst.dialog_start()
assert previous_bs is not bst.bs


def test_update_bst_without_user_act(bst):
"""
Tests whether the BST skips an update if no user act is given.
Args:
bst: BST Object (given in conftest.py)
"""
previous_bs = deepcopy(bst.bs)
bs_dict = bst.update_bst(None)
assert type(bs_dict) == dict
assert len(bs_dict) == 1
assert 'beliefstate' in bs_dict
assert bs_dict['beliefstate'] == bst.bs
assert str(bs_dict['beliefstate']) == str(previous_bs)


def test_update_bst_with_user_act(bst, constraintA):
"""
Tests whether the BST updates its beliefstate if a user act is given.
Args:
bst: BST Object (given in conftest.py)
constraintA (dict): an existing slot-value pair in the domain (given in
conftest_<domain>.py)
"""
user_acts = [UserAct(act_type=UserActionType.Inform, slot=constraintA['slot'],
value=constraintA['value'])]
previous_bs = deepcopy(bst.bs)
bs_dict = bst.update_bst(user_acts)
assert type(bs_dict) == dict
assert len(bs_dict) == 1
assert 'beliefstate' in bs_dict
assert bs_dict['beliefstate'] == bst.bs
assert 'user_acts' in bs_dict['beliefstate']
assert 'num_matches' in bs_dict['beliefstate']
assert 'discriminable' in bs_dict['beliefstate']
assert str(bs_dict['beliefstate']) != str(previous_bs)


def test_update_bst_sets_number_of_db_matches(bst, constraintA):
"""
Tests whether updating the BST also updates the number of matches in the database and whether they are discriminable.
Args:
bst: BST Object (given in conftest.py)
constraintA (dict): an existing slot-value pair in the domain (given in
conftest_<domain>.py)
"""
bst.bs['informs'][constraintA['slot']] = {constraintA['value']: 0.5}
user_acts = [UserAct(act_type=UserActionType.Hello)]
bs_dict = bst.update_bst(user_acts)
assert 'num_matches' in bs_dict['beliefstate']
assert type(bs_dict['beliefstate']['num_matches']) == int
assert bs_dict['beliefstate']['num_matches'] > -1
assert 'discriminable' in bs_dict['beliefstate']
assert type(bs_dict['beliefstate']['discriminable']) == bool


def test_reset_informs_removes_slots_from_informs(bst):
"""
Tests whether reset informs removes all given inform slots.
Args:
bst: BST Object (given in conftest.py)
"""
acts = [UserAct(act_type=UserActionType.Inform, slot='foo', value='bar'),
UserAct(act_type=UserActionType.Inform, slot='bar', value='foo')]
bst.bs['informs'] = {'foo': {'bar': 0.5}, 'bar': {'foo': 0.3}, 'baz': {'foo': 0.6}}
bst._reset_informs(acts)
assert all(act.slot not in bst.bs['informs'] for act in acts)


def test_reset_informs_resets_only_informs(bst):
"""
Tests whether reset informs removes only inform slots.
Args:
bst: BST Object (given in conftest.py)
"""
acts = [UserAct(act_type=UserActionType.Inform, slot='foo', value='bar'),
UserAct(act_type=UserActionType.Request, slot='bar', value='foo')]
bst.bs['informs'] = {'foo': {'bar': 0.5}, 'bar': {'foo': 0.3}, 'baz': {'foo': 0.6}}
bst._reset_informs(acts)
assert all(act.slot not in bst.bs['informs'] for act in acts if act.type ==
UserActionType.Inform)
assert 'bar' in bst.bs['informs']


def test_reset_requests(bst):
"""
Tests resetting the requests dict of the beliefstate.
Args:
bst: BST Object (given in conftest.py)
"""
bst.bs['requests']['foo'] = 0.5
bst._reset_requests()
assert bst.bs['requests'] == {}


def test_get_all_usr_action_types(bst):
"""
Tests whether requesting user action types will return all of them.
Args:
bst: BST Object (given in conftest.py)
"""
user_action_types = [UserActionType.Inform, UserActionType.Request, UserActionType.Hello,
UserActionType.Thanks]
user_acts = [UserAct(act_type=act_type) for act_type in user_action_types]
act_type_set = bst._get_all_usr_action_types(user_acts)
assert act_type_set == set(user_action_types)


def test_handle_user_acts_resets_informs_about_primary_key_for_inform_act(bst):
"""
Tests whether the informs about the primary key reset when handling a user inform.
Args:
bst: BST Object (given in conftest.py)
"""
bst.bs['informs'][bst.domain.get_primary_key()] = {'foo': 0.5}
bst.bs['user_acts'] = [UserActionType.Inform]
bst._handle_user_acts([])
assert bst.domain.get_primary_key() not in bst.bs['informs']


def test_handle_user_acts_resets_informs_and_request_for_select_domain_act(bst):
"""
Tests whether the informs and requests reset when handling a new domain selection by the user.
Args:
bst: BST Object (given in conftest.py)
"""
bst.bs['informs']['foo'] = {'bar': 0.5}
bst.bs['requests']['foo'] = 0.5
bst.bs['user_acts'] = [UserActionType.SelectDomain]
bst._handle_user_acts([])
assert bst.bs['informs'] == {}
assert bst.bs['requests'] == {}


def test_handle_user_acts_for_user_request(bst):
"""
Tests whether the requests are set correctly when handling a user request.
Args:
bst: BST Object (given in conftest.py)
"""
slot = 'foo'
score = 0.5
user_acts = [UserAct(act_type=UserActionType.Request, slot=slot, score=score)]
bst._handle_user_acts(user_acts)
assert slot in bst.bs['requests']
assert bst.bs['requests'][slot] == score


def test_handle_user_acts_for_user_inform(bst):
"""
Tests whether the informs are set correctly when handling a user inform.
Args:
bst: BST Object (given in conftest.py)
"""
slot = 'foo'
value = 'bar'
score = 0.5
user_acts = [UserAct(act_type=UserActionType.Inform, slot=slot, value=value, score=score)]
bst._handle_user_acts(user_acts)
assert slot in bst.bs['informs']
assert value in bst.bs['informs'][slot]
assert bst.bs['informs'][slot][value] == score


def test_handle_user_acts_for_user_negative_inform(bst):
"""
Tests whether handling a negative inform by the user deletes the corresponding inform value.
Args:
bst: BST Object (given in conftest.py)
"""
slot = 'foo'
value = 'bar'
bst.bs['informs'][slot] = {value: 0.5}
user_acts = [UserAct(act_type=UserActionType.NegativeInform, slot=slot, value=value)]
bst._handle_user_acts(user_acts)
assert value not in bst.bs['informs'][slot]


def test_handle_user_acts_for_user_request_alternatives(bst, primkey_constraint):
"""
Tests whether the primary key is removed from the informs when handling a user request for
alternatives.
Args:
bst: BST Object (given in conftest.py)
primkey_constraint (dict): slot-value pair for a primary key constraint (given in
conftest_<domain>.py)
"""
bst.bs['informs'][primkey_constraint['slot']] = {primkey_constraint['value']: 0.5}
user_acts = [UserAct(act_type=UserActionType.RequestAlternatives)]
bst._handle_user_acts(user_acts)
assert bst.domain.get_primary_key() not in bst.bs['informs']
70 changes: 70 additions & 0 deletions adviser/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import os
import sys
import argparse
import pytest

def get_root_dir():
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

sys.path.append(get_root_dir())
from utils.beliefstate import BeliefState
from utils.domain.jsonlookupdomain import JSONLookupDomain
from services.policy import HandcraftedPolicy
from services.bst import HandcraftedBST
from services.simulator.goal import Goal
from services.nlu.nlu import HandcraftedNLU
from services.nlg import HandcraftedNLG
from services.nlg.affective_nlg import HandcraftedEmotionNLG

from services.simulator.simulator import HandcraftedUserSimulator, Agenda


pytest_plugins = ['conftest_superhero']


@pytest.fixture
def domain(domain_name):
return JSONLookupDomain(domain_name)

@pytest.fixture
def beliefstate(domain):
bs = BeliefState(domain)
return bs

@pytest.fixture
def policy(domain):
policy = HandcraftedPolicy(domain)
policy.dialog_start()
return policy

@pytest.fixture
def bst(domain):
return HandcraftedBST(domain)

@pytest.fixture
def goal(domain):
return Goal(domain)

@pytest.fixture
def nlu(domain):
nlu = HandcraftedNLU(domain)
return nlu

@pytest.fixture
def nlg(domain):
nlg = HandcraftedNLG(domain)
return nlg

@pytest.fixture
def aff_nlg(domain):
aff_nlg = HandcraftedEmotionNLG(domain)
return aff_nlg

def agenda():
return Agenda()

@pytest.fixture
def simulator(domain):
simulator = HandcraftedUserSimulator(domain)
simulator.dialog_start()
return simulator
Loading

0 comments on commit 13d74be

Please sign in to comment.