import pytest from unittest.mock import patch, call, Mock from mlagents.trainers.meta_curriculum import MetaCurriculum from mlagents.trainers.exception import MetaCurriculumError class MetaCurriculumTest(MetaCurriculum): """This class allows us to test MetaCurriculum objects without calling MetaCurriculum's __init__ function. """ def __init__(self, brains_to_curriculums): self._brains_to_curriculums = brains_to_curriculums @pytest.fixture def default_reset_parameters(): return {'param1' : 1, 'param2' : 2, 'param3' : 3} @pytest.fixture def more_reset_parameters(): return {'param4' : 4, 'param5' : 5, 'param6' : 6} @pytest.fixture def measure_vals(): return {'Brain1' : 0.2, 'Brain2' : 0.3} @pytest.fixture def reward_buff_sizes(): return {'Brain1' : 7, 'Brain2' : 8} @patch('mlagents.trainers.Curriculum.get_config', return_value={}) @patch('mlagents.trainers.Curriculum.__init__', return_value=None) @patch('os.listdir', return_value=['Brain1.json', 'Brain2.json']) def test_init_meta_curriculum_happy_path(listdir, mock_curriculum_init, mock_curriculum_get_config, default_reset_parameters): meta_curriculum = MetaCurriculum('test/', default_reset_parameters) assert len(meta_curriculum.brains_to_curriculums) == 2 assert 'Brain1' in meta_curriculum.brains_to_curriculums assert 'Brain2' in meta_curriculum.brains_to_curriculums calls = [call('test/Brain1.json', default_reset_parameters), call('test/Brain2.json', default_reset_parameters)] mock_curriculum_init.assert_has_calls(calls) @patch('os.listdir', side_effect=NotADirectoryError()) def test_init_meta_curriculum_bad_curriculum_folder_raises_error(listdir): with pytest.raises(MetaCurriculumError): MetaCurriculum('test/', default_reset_parameters) @patch('mlagents.trainers.Curriculum') @patch('mlagents.trainers.Curriculum') def test_set_lesson_nums(curriculum_a, curriculum_b): meta_curriculum = MetaCurriculumTest({'Brain1' : curriculum_a, 'Brain2' : curriculum_b}) meta_curriculum.lesson_nums = {'Brain1' : 1, 'Brain2' : 3} assert curriculum_a.lesson_num == 1 assert curriculum_b.lesson_num == 3 @patch('mlagents.trainers.Curriculum') @patch('mlagents.trainers.Curriculum') def test_increment_lessons(curriculum_a, curriculum_b, measure_vals): meta_curriculum = MetaCurriculumTest({'Brain1' : curriculum_a, 'Brain2' : curriculum_b}) meta_curriculum.increment_lessons(measure_vals) curriculum_a.increment_lesson.assert_called_with(0.2) curriculum_b.increment_lesson.assert_called_with(0.3) @patch('mlagents.trainers.Curriculum') @patch('mlagents.trainers.Curriculum') def test_increment_lessons_with_reward_buff_sizes(curriculum_a, curriculum_b, measure_vals, reward_buff_sizes): curriculum_a.min_lesson_length = 5 curriculum_b.min_lesson_length = 10 meta_curriculum = MetaCurriculumTest({'Brain1' : curriculum_a, 'Brain2' : curriculum_b}) meta_curriculum.increment_lessons(measure_vals, reward_buff_sizes=reward_buff_sizes) curriculum_a.increment_lesson.assert_called_with(0.2) curriculum_b.increment_lesson.assert_not_called() @patch('mlagents.trainers.Curriculum') @patch('mlagents.trainers.Curriculum') def test_set_all_curriculums_to_lesson_num(curriculum_a, curriculum_b): meta_curriculum = MetaCurriculumTest({'Brain1' : curriculum_a, 'Brain2' : curriculum_b}) meta_curriculum.set_all_curriculums_to_lesson_num(2) assert curriculum_a.lesson_num == 2 assert curriculum_b.lesson_num == 2 @patch('mlagents.trainers.Curriculum') @patch('mlagents.trainers.Curriculum') def test_get_config(curriculum_a, curriculum_b, default_reset_parameters, more_reset_parameters): curriculum_a.get_config.return_value = default_reset_parameters curriculum_b.get_config.return_value = default_reset_parameters meta_curriculum = MetaCurriculumTest({'Brain1' : curriculum_a, 'Brain2' : curriculum_b}) assert meta_curriculum.get_config() == default_reset_parameters curriculum_b.get_config.return_value = more_reset_parameters new_reset_parameters = dict(default_reset_parameters) new_reset_parameters.update(more_reset_parameters) assert meta_curriculum.get_config() == new_reset_parameters