Exemple #1
0
 def test_equality_and_copy_operators(self):
     tt = TimeTable()
     
     fill_timetable(self.timetable)
     fill_timetable(tt)
     
     self.assertEqual(self.timetable, tt)
     self.assertEqual(self.timetable.mode, tt.mode)
     
     # test copy operators
     tt2 = copy.deepcopy(self.timetable)
     self.assertEqual(self.timetable, tt2)
     self.assertEqual(self.timetable.mode, tt2.mode)
     
     # changing the mode makes two timetable different
     tt2.mode = timetable.JSON_MODE_OFF
     self.assertNotEqual(self.timetable, tt2)
     self.assertNotEqual(self.timetable.mode, tt2.mode)
     
     # change other settings and test inequality
     tt.hvac_mode = timetable.JSON_HVAC_COOLING
     self.assertNotEqual(self.timetable, tt)
     self.assertNotEqual(self.timetable.hvac_mode, tt.hvac_mode)
     
     tt2.update(3,15,0,34)
     self.assertNotEqual(self.timetable, tt2)
Exemple #2
0
 def test_save_and_load(self):
     # temporary file to save data
     (file1,filepath1) = tempfile.mkstemp(suffix='.tmp', prefix='thermod')
     (file2,filepath2) = tempfile.mkstemp(suffix='.tmp', prefix='thermod')
     
     # timetable has no filepath cannot be saved
     self.assertRaises(RuntimeError,self.timetable.save)
     
     # setting filepath
     self.timetable.filepath = filepath1
     
     # timetable is empty cannot be saved
     self.assertRaises(ValidationError,self.timetable.save)
     
     # filling the timetable
     fill_timetable(self.timetable)
     
     # saving and loading two times
     self.timetable.save()
     tt1 = TimeTable(filepath1)
     self.assertEqual(self.timetable, tt1)
     self.assertEqual(self.timetable.mode, tt1.mode)
     
     tt1.save(filepath2)
     tt2 = TimeTable()
     tt2.filepath = filepath2
     tt2.reload()
     self.assertEqual(self.timetable, tt2)
     self.assertEqual(self.timetable.mode, tt2.mode)
     
     # removing temporary files
     os.close(file1)
     os.remove(filepath1)
     os.close(file2)
     os.remove(filepath2)
Exemple #3
0
 def test_equality_regardless_of_inertia(self):
     tt = TimeTable(inertia=1)
     
     fill_timetable(self.timetable)
     fill_timetable(tt)
     
     self.assertEqual(self.timetable, tt)
     
     # now change inertia
     tt._inertia = 2
     self.assertEqual(self.timetable, tt)
 
     # now change again
     tt._inertia = 3
     self.assertEqual(self.timetable, tt)
Exemple #4
0
 async def this_test():
     async with aiohttp.ClientSession() as session:
         # wrong url
         async with session.get('http://localhost:4345/wrong') as wrong:
             self.assertEqual(wrong.status, 404)
         
         # right url
         async with session.get(__url_settings__) as r:
             self.assertEqual(r.status, 200)
             settings = await r.json()
         
         # check returned settings
         tt = TimeTable()
         tt.__setstate__(settings)
         self.assertEqual(self.timetable, tt)
Exemple #5
0
    def setUp(self):
        self.lock = asyncio.Condition()

        self.timetable = TimeTable()
        fill_timetable(self.timetable)
        self.timetable.filepath = os.path.join(tempfile.gettempdir(),
                                               'timetable.json')
        self.timetable.save()

        self.heating = BaseHeating()
        self.cooling = BaseCooling()
        self.thermometer = FakeThermometer()

        self.socket = SocketThread(self.timetable, self.heating, self.cooling,
                                   self.thermometer, self.lock)
        self.socket.start()
Exemple #6
0
    def test_get_settings(self):
        # wrong url
        wrong = requests.get('http://localhost:4345/wrong')
        self.assertEqual(wrong.status_code, 404)
        wrong.close()

        # right url
        r = requests.get(__url_settings__)
        self.assertEqual(r.status_code, 200)
        settings = r.json()
        r.close()

        # check returned settings
        tt = TimeTable()
        tt.__setstate__(settings)
        self.assertEqual(self.timetable, tt)
Exemple #7
0
    def setUp(self):
        self.timetable = TimeTable()
        fill_timetable(self.timetable)

        self.mttable = MementoTable()
        fill_timetable(self.mttable)

        self.heating = BaseHeating()
Exemple #8
0
 def setUp(self):
     self.loop = asyncio.get_event_loop()
     self.lock = asyncio.Condition()
     
     self.timetable = TimeTable()
     fill_timetable(self.timetable)
     self.timetable.filepath = os.path.join(tempfile.gettempdir(), 'timetable.json')
     self.timetable.save()
     
     self.heating = BaseHeating()
     self.thermometer = FakeThermometer()
     
     self.socket = ControlSocket(self.timetable,
                                 self.heating,
                                 self.thermometer,
                                 'localhost',
                                 4345,  # using different port to run test while real thermod is running
                                 self.lock)
     self.loop.run_until_complete(self.socket.start())
Exemple #9
0
class TestSocket(unittest.TestCase):
    """Test cases for `thermod.socket` module."""
    def setUp(self):
        self.lock = asyncio.Condition()

        self.timetable = TimeTable()
        fill_timetable(self.timetable)
        self.timetable.filepath = os.path.join(tempfile.gettempdir(),
                                               'timetable.json')
        self.timetable.save()

        self.heating = BaseHeating()
        self.cooling = BaseCooling()
        self.thermometer = FakeThermometer()

        self.socket = SocketThread(self.timetable, self.heating, self.cooling,
                                   self.thermometer, self.lock)
        self.socket.start()

    def tearDown(self):
        self.socket.stop()
        self.socket.join()
        os.remove(self.timetable.filepath)

    def test_get_settings(self):
        # wrong url
        wrong = requests.get('http://localhost:4345/wrong')
        self.assertEqual(wrong.status_code, 404)
        wrong.close()

        # right url
        r = requests.get(__url_settings__)
        self.assertEqual(r.status_code, 200)
        settings = r.json()
        r.close()

        # check returned settings
        tt = TimeTable()
        tt.__setstate__(settings)
        self.assertEqual(self.timetable, tt)

    def test_get_heating(self):
        # wrong url
        wrong = requests.get('http://localhost:4345/wrong')
        self.assertEqual(wrong.status_code, 404)
        wrong.close()

        # right url
        r = requests.get(__url_heating__)
        self.assertEqual(r.status_code, 200)
        heating = r.json()
        r.close()

        # check returned heating informations
        self.assertEqual(heating['status'], self.heating.status)
        self.assertAlmostEqual(heating['current_temperature'],
                               self.thermometer.temperature,
                               delta=0.1)
        self.assertEqual(heating['target_temperature'],
                         self.timetable.target_temperature())

    def test_post_wrong_messages(self):
        # wrong url
        wrong = requests.post('http://localhost:4345/wrong', {})
        self.assertEqual(wrong.status_code, 404)
        wrong.close()

        # wrong value for status
        wrong = requests.post(__url_settings__,
                              {socket.REQ_SETTINGS_MODE: 'invalid'})
        self.assertEqual(wrong.status_code, 400)
        wrong.close()

        # wrong value (greater then max allowed)
        wrong = requests.post(__url_settings__,
                              {socket.REQ_SETTINGS_DIFFERENTIAL: 1.1})
        self.assertEqual(wrong.status_code, 400)
        wrong.close()

        # wrong JSON data for settings
        settings = self.timetable.__getstate__()
        settings[timetable.JSON_TEMPERATURES][timetable.JSON_TMAX_STR] = 'inf'
        wrong = requests.post(__url_settings__,
                              {socket.REQ_SETTINGS_ALL: settings})
        self.assertEqual(wrong.status_code, 400)
        wrong.close()

        # invalid JSON syntax for settings
        settings = self.timetable.settings()
        wrong = requests.post(__url_settings__,
                              {socket.REQ_SETTINGS_ALL: settings[0:30]})
        self.assertEqual(wrong.status_code, 400)
        wrong.close()

        # check original paramethers
        self.assertAlmostEqual(self.timetable.differential, 0.5, delta=0.01)
        self.assertAlmostEqual(self.timetable.tmax, 21, delta=0.01)

    def test_post_right_messages(self):
        # single settings
        p = requests.post(__url_settings__,
                          {socket.REQ_SETTINGS_MODE: timetable.JSON_MODE_OFF})
        self.assertEqual(p.status_code, 200)
        self.assertEqual(self.timetable.mode, timetable.JSON_MODE_OFF)
        p.close()

        # multiple settings
        q = requests.post(
            __url_settings__, {
                socket.REQ_SETTINGS_MODE: timetable.JSON_MODE_TMAX,
                socket.REQ_SETTINGS_TMAX: 32.3,
                socket.REQ_SETTINGS_GRACE_TIME: 'inf'
            })

        self.assertEqual(q.status_code, 200)
        self.assertEqual(self.timetable.mode, timetable.JSON_MODE_TMAX)
        self.assertAlmostEqual(self.timetable.tmax, 32.3, delta=0.01)
        self.assertEqual(self.timetable.grace_time, float('inf'))
        q.close()

        # some days
        old_set = self.timetable.__getstate__()
        friday = old_set[timetable.JSON_TIMETABLE][timetable.json_get_day_name(
            5)]
        sunday = old_set[timetable.JSON_TIMETABLE][timetable.json_get_day_name(
            7)]

        friday['h12'][0] = 44
        friday['h15'][1] = 45
        sunday['h06'][2] = 46
        sunday['h07'][3] = 47

        # all settings
        tt2 = copy.deepcopy(self.timetable)
        tt2.mode = timetable.JSON_MODE_TMAX
        tt2.grace_time = 3600
        tt2.update('thursday', 4, 1, 36.5)

        self.assertNotEqual(self.timetable, tt2)  # different before update

        s = requests.post(__url_settings__,
                          {socket.REQ_SETTINGS_ALL: tt2.settings()})
        self.assertEqual(s.status_code, 200)
        s.close()

        self.assertEqual(self.timetable, tt2)  # equal after update

    def test_unsupported_http_methods(self):
        pa = requests.patch(__url_settings__, {})
        self.assertEqual(pa.status_code, 501)
        pa.close()

        pu = requests.put(__url_settings__, {})
        self.assertEqual(pu.status_code, 501)
        pu.close()

        de = requests.delete(__url_heating__)
        self.assertEqual(de.status_code, 501)
        de.close()
Exemple #10
0
 def setUp(self):
     self.timetable = TimeTable()
     self.heating = BaseHeating()
Exemple #11
0
class TestTimeTable(aiounittest.AsyncTestCase):
    """Test cases for `TimeTable` class."""

    def setUp(self):
        self.timetable = TimeTable()
        self.heating = BaseHeating()
    
    def tearDown(self):
        pass
    
    def test_filepath(self):
        # empty tiletable
        self.assertRaises(RuntimeError, self.timetable.reload)
        self.assertRaises(RuntimeError, self.timetable.save)
        
        # non existing file
        with self.assertRaises(FileNotFoundError):
            self.timetable.filepath = '/tmp/non_existing_file'
            self.timetable.reload()
        
        # invalid json file
        invalid_json_file = os.path.join(tempfile.gettempdir(), 'thermod-invalid-json.conf')
        
        with open(invalid_json_file, 'w') as file:
            file.write('[global] invalid = not json')
        
        with self.assertRaises(ValueError):
            self.timetable.filepath = invalid_json_file
            self.timetable.reload()
        
        try:
            os.remove(invalid_json_file)
        except FileNotFoundError:
            pass
    
    
    def test_mode(self):
        for mode in timetable.JSON_ALL_MODES:
            self.timetable.mode = mode
            self.assertEqual(mode, self.timetable.mode)
        
        mode = 'invalid'
        self.assertNotIn(mode, timetable.JSON_ALL_MODES)
        with self.assertRaises(JsonValueError):
            self.timetable.mode = mode
    
    
    def test_differential(self):
        # valid float values (rounded up to 1st decimal number)
        for d in range(0,101):
            diff = d/100
            self.timetable.differential = diff
            self.assertAlmostEqual(round(diff,1),
                                   self.timetable.differential,
                                   places=1)
        
        # valid string values (rounded up to 1st decimal number)
        for d in range(0,101):
            diff = d/100
            self.timetable.differential = format(diff, '+.2f')
            self.assertAlmostEqual(round(diff,1),
                                   self.timetable.differential,
                                   places=1)
        
        # invalid values
        with self.assertRaises(JsonValueError):
            self.timetable.differential = 1.1
        
        with self.assertRaises(JsonValueError):
            self.timetable.differential = -0.15
        
        with self.assertRaises(JsonValueError):
            self.timetable.differential = 'invalid'
    
    
    def test_t0(self):
        # valid float values (rounded up to 1st decimal number)
        for t in range(100,150):
            temp = t/10
            self.timetable.t0 = temp
            self.assertAlmostEqual(temp, self.timetable.t0, places=1)
        
        # valid string values (rounded up to 1st decimal number)
        for t in range(100,150):
            temp = t/10
            self.timetable.t0 = format(temp, '+.1f')
            self.assertAlmostEqual(temp, self.timetable.t0, places=1)
        
        # invalid values
        for t in timetable.JSON_ALL_TEMPERATURES:
            with self.assertRaises(JsonValueError):
                self.timetable.t0 = t
        
        with self.assertRaises(JsonValueError):
            self.timetable.t0 = 'invalid'
        
        with self.assertRaises(JsonValueError):
            self.timetable.tmax = 'nan'
    
    
    def test_tmin(self):
        # valid float values (rounded up to 1st decimal number)
        for t in range(130,180):
            temp = t/10
            self.timetable.tmin = temp
            self.assertAlmostEqual(temp, self.timetable.tmin, places=1)
        
        # valid string values (rounded up to 1st decimal number)
        for t in range(130,180):
            temp = t/10
            self.timetable.tmin = format(temp, '+.1f')
            self.assertAlmostEqual(temp, self.timetable.tmin, places=1)
        
        # invalid values
        for t in timetable.JSON_ALL_TEMPERATURES:
            with self.assertRaises(JsonValueError):
                self.timetable.tmin = t
        
        with self.assertRaises(JsonValueError):
            self.timetable.tmin = 'invalid'
        
        with self.assertRaises(JsonValueError):
            self.timetable.tmax = '-inf'
    
    
    def test_tmax(self):
        # valid float values (rounded up to 1st decimal number)
        for t in range(170,210):
            temp = t/10
            self.timetable.tmax = temp
            self.assertAlmostEqual(temp, self.timetable.tmax, places=1)
        
        # valid string values (rounded up to 1st decimal number)
        for t in range(170,210):
            temp = t/10
            self.timetable.tmax = format(temp, '+.1f')
            self.assertAlmostEqual(temp, self.timetable.tmax, places=1)
        
        # invalid values
        for t in timetable.JSON_ALL_TEMPERATURES:
            with self.assertRaises(JsonValueError):
                self.timetable.tmax = t
        
        with self.assertRaises(JsonValueError):
            self.timetable.tmax = 'invalid'
        
        with self.assertRaises(JsonValueError):
            self.timetable.tmax = 'inf'
    
    
    def test_hvac_mode(self):
        self.assertEqual(self.timetable.hvac_mode, HVAC_HEATING)
        
        self.timetable.hvac_mode = HVAC_COOLING
        self.assertEqual(self.timetable.hvac_mode, HVAC_COOLING)
        
        with self.assertRaises(JsonValueError):
            self.timetable.hvac_mode = 'othervalue'
    
    
    def test_degrees(self):
        # no main temperatures already set, thus exception
        self.assertRaises(RuntimeError,self.timetable.degrees, 't0')
        
        # set main temperatures
        self.timetable.t0 = 5
        self.timetable.tmin = 17
        self.timetable.tmax = 21
        
        self.assertEqual(self.timetable.degrees(timetable.JSON_T0_STR), 5)
        self.assertEqual(self.timetable.degrees(timetable.JSON_TMIN_STR), 17)
        self.assertEqual(self.timetable.degrees(timetable.JSON_TMAX_STR), 21)
        
        for temp in range(50):
            self.assertEqual(self.timetable.degrees(temp), temp)
            self.assertEqual(self.timetable.degrees(format(temp,'-.1f')), temp)
            
            self.timetable.tmax = temp
            self.assertEqual(self.timetable.degrees('tmax'), temp)
    
    
    def test_save_and_load(self):
        # temporary file to save data
        (file1,filepath1) = tempfile.mkstemp(suffix='.tmp', prefix='thermod')
        (file2,filepath2) = tempfile.mkstemp(suffix='.tmp', prefix='thermod')
        
        # timetable has no filepath cannot be saved
        self.assertRaises(RuntimeError,self.timetable.save)
        
        # setting filepath
        self.timetable.filepath = filepath1
        
        # timetable is empty cannot be saved
        self.assertRaises(ValidationError,self.timetable.save)
        
        # filling the timetable
        fill_timetable(self.timetable)
        
        # saving and loading two times
        self.timetable.save()
        tt1 = TimeTable(filepath1)
        self.assertEqual(self.timetable, tt1)
        self.assertEqual(self.timetable.mode, tt1.mode)
        
        tt1.save(filepath2)
        tt2 = TimeTable()
        tt2.filepath = filepath2
        tt2.reload()
        self.assertEqual(self.timetable, tt2)
        self.assertEqual(self.timetable.mode, tt2.mode)
        
        # removing temporary files
        os.close(file1)
        os.remove(filepath1)
        os.close(file2)
        os.remove(filepath2)
    
    
    def test_equality_and_copy_operators(self):
        tt = TimeTable()
        
        fill_timetable(self.timetable)
        fill_timetable(tt)
        
        self.assertEqual(self.timetable, tt)
        self.assertEqual(self.timetable.mode, tt.mode)
        
        # test copy operators
        tt2 = copy.deepcopy(self.timetable)
        self.assertEqual(self.timetable, tt2)
        self.assertEqual(self.timetable.mode, tt2.mode)
        
        # changing the mode makes two timetable different
        tt2.mode = timetable.JSON_MODE_OFF
        self.assertNotEqual(self.timetable, tt2)
        self.assertNotEqual(self.timetable.mode, tt2.mode)
        
        # change other settings and test inequality
        tt.hvac_mode = timetable.JSON_HVAC_COOLING
        self.assertNotEqual(self.timetable, tt)
        self.assertNotEqual(self.timetable.hvac_mode, tt.hvac_mode)
        
        tt2.update(3,15,0,34)
        self.assertNotEqual(self.timetable, tt2)
    
    
    def test_equality_regardless_of_inertia(self):
        tt = TimeTable(inertia=1)
        
        fill_timetable(self.timetable)
        fill_timetable(tt)
        
        self.assertEqual(self.timetable, tt)
        
        # now change inertia
        tt._inertia = 2
        self.assertEqual(self.timetable, tt)
    
        # now change again
        tt._inertia = 3
        self.assertEqual(self.timetable, tt)
    
    
    def test_update(self):
        # TODO this test is not reliable, need improvments or revisitation
        self.assertRaises(JsonValueError, self.timetable.update, 'invalid', 10, 0, timetable.JSON_TMAX_STR)
        self.assertRaises(JsonValueError, self.timetable.update, 8, 10, 0, timetable.JSON_T0_STR)
        self.assertRaises(JsonValueError, self.timetable.update, 4, 'invalid', 0, timetable.JSON_TMIN_STR)
        self.assertRaises(JsonValueError, self.timetable.update, 4, 23, 5, timetable.JSON_TMAX_STR)
        self.assertRaises(JsonValueError, self.timetable.update, 4, 26, 2, timetable.JSON_TMIN_STR)
        self.assertRaises(JsonValueError, self.timetable.update, 7, 11, 1, 'invalid')
        self.assertRaises(JsonValueError, self.timetable.update, 4, 11, 'invalid', timetable.JSON_TMAX_STR)
    
    
    def test_locale(self):
        # setlocale doesn't work on Windows
        if os.name == 'posix':
            locale.setlocale(locale.LC_ALL,'it_IT.utf8')
            
            fill_timetable(self.timetable)
            
            self.assertIsNone(self.timetable.update('Lun',10,1,30))
            self.assertIsNone(self.timetable.update('mer',11,2,20))
            settings = self.timetable.__getstate__()
            
            day = timetable.json_get_day_name(1)
            hour = timetable.json_format_hour(10)
            quarter = 1
            t1 = timetable.temperature_to_float(settings[timetable.JSON_TIMETABLE][day][hour][quarter])
            t2 = timetable.temperature_to_float(30)
            self.assertEqual(t1, t2)
    
            day = timetable.json_get_day_name(3)
            hour = timetable.json_format_hour(11)
            quarter = 2
            t1 = timetable.temperature_to_float(settings[timetable.JSON_TIMETABLE][day][hour][quarter])
            t2 = timetable.temperature_to_float(20)
            self.assertEqual(t1, t2)
    
    
    async def test_timetable_01(self):  # test t0 mode
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_T0
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        
        for temp in range(-10,5):
            self.assertTrue(self.timetable.should_the_heating_be_on(temp, await self.heating.status))
        
        # Not testing temp equal to t0 (5 degrees) because the result
        # depends by both differential and `ison` internal state.
        
        for temp in range(6,15):
            self.assertFalse(self.timetable.should_the_heating_be_on(temp, await self.heating.status))
    
    
    async def test_timetable_02(self):  # test tmin mode
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_TMIN
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        
        for temp in range(0,17):
            self.assertTrue(self.timetable.should_the_heating_be_on(temp, await self.heating.status))
        
        # Not testing temp equal to tmin (17 degrees) because the result
        # depends by both differential and `ison` internal state.
        
        for temp in range(18,21):
            self.assertFalse(self.timetable.should_the_heating_be_on(temp, await self.heating.status))
        
    
    async def test_timetable_03(self):  # test tmax mode
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_TMAX
        self.timetable.update(day,hour,quarter,timetable.JSON_T0_STR)
        
        for temp in range(10,21):
            self.assertTrue(self.timetable.should_the_heating_be_on(temp, await self.heating.status))
        
        # Not testing temp equal to tmax (21 degrees) because the result
        # depends by both differential and `ison` internal state.
        
        for temp in range(22,27):
            self.assertFalse(self.timetable.should_the_heating_be_on(temp, await self.heating.status))
    
    
    async def test_timetable_04(self):  # test on/off modes
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_ON
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        self.assertTrue(self.timetable.should_the_heating_be_on(3, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(10, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(22, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(30, await self.heating.status))
        
        self.timetable.mode = timetable.JSON_MODE_OFF
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        self.assertFalse(self.timetable.should_the_heating_be_on(3, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(10, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(22, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(30, await self.heating.status))
    
    
    async def test_timetable_05(self):  # test auto mode
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_AUTO
        
        # current target t0
        self.timetable.update(day,hour,quarter,timetable.JSON_T0_STR)
        self.assertTrue(self.timetable.should_the_heating_be_on(3, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(10, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(22, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(30, await self.heating.status))
        
        # current target tmin
        self.timetable.update(day,hour,quarter,timetable.JSON_TMIN_STR)
        self.assertTrue(self.timetable.should_the_heating_be_on(3, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(10, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(22, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(30, await self.heating.status))
        
        # current target tmax
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        self.assertTrue(self.timetable.should_the_heating_be_on(3, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(10, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(22, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(30, await self.heating.status))
        
        # current target manual temperature
        self.timetable.update(day,hour,quarter,27.5)
        self.assertTrue(self.timetable.should_the_heating_be_on(3, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(10, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(22, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(30, await self.heating.status))
    
    
    async def test_timetable_06(self):
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_AUTO
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        
        # check if the heating should be on
        self.assertTrue(self.timetable.should_the_heating_be_on(19, await self.heating.status))
        
        # virtually switching on and set internal state
        await self.heating.switch_on()
        
        # the temperature start increasing
        self.assertTrue(self.timetable.should_the_heating_be_on(20, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(21, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(21.4, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(21.5, await self.heating.status))
        
        # virtually switching off and set internal state
        await self.heating.switch_off()
        
        # the temperature start decreasing
        self.assertFalse(self.timetable.should_the_heating_be_on(21.4, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(21, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.6, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20, await self.heating.status))
    
    
    async def test_timetable_06b(self):  # the same of test 06 with inertia==2
        fill_timetable(self.timetable)
        self.timetable._inertia = 2
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_AUTO
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        
        # check if the heating should be on
        self.assertTrue(self.timetable.should_the_heating_be_on(19, await self.heating.status))
        
        # virtually switching on and set internal state
        await self.heating.switch_on()
        
        # the temperature start increasing
        self.assertTrue(self.timetable.should_the_heating_be_on(19.5, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.0, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.9, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(21.0, await self.heating.status))  # off at target
        self.assertFalse(self.timetable.should_the_heating_be_on(21.5, await self.heating.status))
        
        # virtually switching off and set internal state
        await self.heating.switch_off()
        
        # the temperature start decreasing
        self.assertFalse(self.timetable.should_the_heating_be_on(21.0, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.1, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.0, await self.heating.status))  # on at target-2*diff
        self.assertTrue(self.timetable.should_the_heating_be_on(19.5, await self.heating.status))
    
    
    async def test_timetable_06c(self):  # the same of test 06 with inertia==3
        fill_timetable(self.timetable)
        self.timetable._inertia = 3
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_AUTO
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        
        # check if the heating should be on
        self.assertTrue(self.timetable.should_the_heating_be_on(19, await self.heating.status))
        
        # virtually switching on and set internal state
        await self.heating.switch_on()
        
        # the temperature start increasing
        self.assertTrue(self.timetable.should_the_heating_be_on(19.5, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.0, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.4, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))  # off at target-diff
        self.assertFalse(self.timetable.should_the_heating_be_on(21.0, await self.heating.status))
        
        # virtually switching off and set internal state
        await self.heating.switch_off()
        
        # the temperature start decreasing
        self.assertFalse(self.timetable.should_the_heating_be_on(21.0, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.1, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.0, await self.heating.status))  # on at target-2*diff
        self.assertTrue(self.timetable.should_the_heating_be_on(19.5, await self.heating.status))
    
    
    async def test_timetable_06_cooling(self):  # # the same of test 06 with hvac_mode==cooling
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_AUTO
        self.timetable.hvac_mode = HVAC_COOLING
        self.timetable.tmax = 30
        self.timetable.tmin = 24
        self.timetable.update(day,hour,quarter,timetable.JSON_TMIN_STR)  # tmin mean 'off' even for cooling
        
        # the cooling should be on if current temp above tmax
        self.assertTrue(self.timetable.should_the_heating_be_on(31, await self.heating.status))
        
        # the cooling should be off for tmin in auto mode
        self.assertFalse(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        
        # the cooling should be on now
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)  # tmax mean 'on' even for cooling
        self.assertTrue(self.timetable.should_the_heating_be_on(25, await self.heating.status))
        
        # virtually switching on and set internal state
        await self.heating.switch_on()
        
        # the temperature start decreasing
        self.assertTrue(self.timetable.should_the_heating_be_on(24.8, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(24.5, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(24.2, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(23.9, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(23.4, await self.heating.status))
        
        # virtually switching off and set internal state
        await self.heating.switch_off()
        
        # the temperature start increasing
        self.assertFalse(self.timetable.should_the_heating_be_on(23.7, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(24.0, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(24.4, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(24.9, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(25.1, await self.heating.status))
    
    
    async def test_timetable_09(self):
        fill_timetable(self.timetable)
        
        now = datetime.now()
        day = now.strftime('%w')
        hour = timetable.json_format_hour(now.hour)
        quarter = int(now.minute // 15)
        
        self.timetable.mode = timetable.JSON_MODE_AUTO
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        await self.heating.switch_on()
        
        # The heating is on and it remains on untill target temperature
        # plus differential
        self.assertTrue(self.timetable.should_the_heating_be_on(21.1, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(21.4, await self.heating.status))
        
        # I simulate the time passing by changing the target temperature
        # next quarter is tmin
        self.timetable.update(day,hour,quarter,timetable.JSON_TMIN_STR)
        self.assertFalse(self.timetable.should_the_heating_be_on(21, await self.heating.status))
        await self.heating.switch_off()
        
        # Next quarter is tmax again, the heating remains off until target minus differential
        self.timetable.update(day,hour,quarter,timetable.JSON_TMAX_STR)
        self.assertFalse(self.timetable.should_the_heating_be_on(21, await self.heating.status))
        self.assertFalse(self.timetable.should_the_heating_be_on(20.6, await self.heating.status))
        self.assertTrue(self.timetable.should_the_heating_be_on(20.5, await self.heating.status))
    
    
    def test_target_temperature(self):
        fill_timetable(self.timetable)
        
        time = datetime(2016,2,14,17,23,0)
        day = time.strftime('%w')
        hour = timetable.json_format_hour(time.hour)
        quarter = int(time.minute // 15)
        
        self.assertAlmostEqual(self.timetable.target_temperature(time), self.timetable.tmax, delta=0.01)
        t = 45.0
        self.timetable.update(day, hour, quarter, t)
        self.assertAlmostEqual(self.timetable.target_temperature(time), t, delta=0.01)
        
        self.assertAlmostEqual(self.timetable.target_temperature(datetime(2016,2,10,9,34,0)), self.timetable.tmin, delta=0.01)
        self.assertAlmostEqual(self.timetable.target_temperature(datetime(2016,2,10,23,34,0)), self.timetable.t0, delta=0.01)
        
        self.timetable.mode = timetable.JSON_MODE_ON
        self.assertEqual(self.timetable.target_temperature(time), None)
        
        self.timetable.mode = timetable.JSON_MODE_OFF
        self.assertEqual(self.timetable.target_temperature(time), None)
        
        self.timetable.mode = timetable.JSON_MODE_TMAX
        self.assertAlmostEqual(self.timetable.target_temperature(time), self.timetable.tmax, delta=0.01)
        
        self.timetable.mode = timetable.JSON_MODE_TMIN
        self.assertAlmostEqual(self.timetable.target_temperature(time), self.timetable.tmin, delta=0.01)
        
        self.timetable.mode = timetable.JSON_MODE_T0
        self.assertAlmostEqual(self.timetable.target_temperature(time), self.timetable.t0, delta=0.01)
    
    def test_load_old_state(self):
        """Try to load on old JSON schema."""
        
        # good old schema
        self.timetable.load(_json_state_v1)
        
        # mixed schema (old with some changes)
        with self.assertRaises(ValidationError):
            self.timetable.load(_json_state_v1.replace('status', 'mode'))
    
    def test_old_state_adapter(self):
        """Check if the adapter is transparent in case of valid new state."""
        self.timetable.load(_json_state_v1)
        state = self.timetable.__getstate__()
        self.assertEqual(state, self.timetable._old_state_adapter(state))
    
    # TODO write more concurrent tests
    def test_threading_01(self):
        fill_timetable(self.timetable)
        
        self.timetable.tmax = 30
        self.timetable.mode = timetable.JSON_MODE_TMAX
        
        loop = asyncio.get_event_loop()
        status = loop.run_until_complete(self.heating.status)
        
        # initial status, the heating should be on
        self.assertTrue(self.timetable.should_the_heating_be_on(20, status))
        
        # creating main lock
        lock = threading.Condition()
        
        # creating updating thread
        thread = threading.Thread(target=self.thread_change_mode, args=(lock,))
        
        # the lock is acquired, then the thread that changes a parameter is
        # executed and the assert is checked again, if the lock works the
        # assert is still True
        with lock:
            thread.start()
            self.assertTrue(self.timetable.should_the_heating_be_on(20, status))

        # the assert become False after the execution of the thread
        thread.join()
        self.assertFalse(self.timetable.should_the_heating_be_on(20, status))
    
    def thread_change_mode(self, lock):
        loop = asyncio.new_event_loop()
        status = loop.run_until_complete(self.heating.status)
        
        self.assertTrue(self.timetable.should_the_heating_be_on(20, status))
    
        with lock:
            self.timetable.mode = timetable.JSON_MODE_OFF
            self.assertFalse(self.timetable.should_the_heating_be_on(20, status))
    
    def test_should_be_on(self):
        s1 = ShouldBeOn(True)
        self.assertTrue(s1)
        
        st2 = ThermodStatus(time.time(), timetable.JSON_MODE_AUTO, 1, 5, 10)
        s2 = ShouldBeOn(False, st2)
        self.assertFalse(s2)
        
        st3 = ThermodStatus(time.time(), timetable.JSON_MODE_ON, 0, 17, 21)
        s3 = ShouldBeOn(True, st3)
        self.assertEqual(s1, s3)

        self.assertFalse(s1 and s2)
Exemple #12
0
class TestSocket(unittest.TestCase):
    """Test cases for `thermod.socket` module."""
    
    def setUp(self):
        self.loop = asyncio.get_event_loop()
        self.lock = asyncio.Condition()
        
        self.timetable = TimeTable()
        fill_timetable(self.timetable)
        self.timetable.filepath = os.path.join(tempfile.gettempdir(), 'timetable.json')
        self.timetable.save()
        
        self.heating = BaseHeating()
        self.thermometer = FakeThermometer()
        
        self.socket = ControlSocket(self.timetable,
                                    self.heating,
                                    self.thermometer,
                                    'localhost',
                                    4345,  # using different port to run test while real thermod is running
                                    self.lock)
        self.loop.run_until_complete(self.socket.start())
    
    
    def tearDown(self):
        self.loop.run_until_complete(self.socket.stop())
        os.remove(self.timetable.filepath)
    
    
    def test_get_settings(self):
        async def this_test():
            async with aiohttp.ClientSession() as session:
                # wrong url
                async with session.get('http://localhost:4345/wrong') as wrong:
                    self.assertEqual(wrong.status, 404)
                
                # right url
                async with session.get(__url_settings__) as r:
                    self.assertEqual(r.status, 200)
                    settings = await r.json()
                
                # check returned settings
                tt = TimeTable()
                tt.__setstate__(settings)
                self.assertEqual(self.timetable, tt)
        
        self.loop.run_until_complete(this_test())
    
    
    def test_get_heating(self):
        async def this_test():
            async with aiohttp.ClientSession() as session:
                # wrong url
                async with session.get('http://localhost:4345/wrong') as wrong:
                    self.assertEqual(wrong.status, 404)
                
                # right url
                async with session.get(__url_heating__) as r:
                    self.assertEqual(r.status, 200)
                    heating = await r.json()
                
                # check returned heating informations
                self.assertEqual(heating['mode'], self.timetable.mode)
                self.assertEqual(heating['status'], await self.heating.status)
                self.assertAlmostEqual(heating['current_temperature'], await self.thermometer.temperature, delta=0.1)
                self.assertEqual(heating['target_temperature'], self.timetable.target_temperature())
    
        self.loop.run_until_complete(this_test())
    
    
    def test_post_wrong_messages(self):
        async def this_test():
            async with aiohttp.ClientSession() as session:
                # wrong url
                async with session.post('http://localhost:4345/wrong', data={}) as wrong:
                    self.assertEqual(wrong.status, 404)
                
                # wrong value for status
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_MODE: 'invalid'}) as wrong:
                    self.assertEqual(wrong.status, 400)
                
                # wrong value (greater then max allowed)
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_DIFFERENTIAL: 1.1}) as wrong:
                    self.assertEqual(wrong.status, 400)
                
                # wrong value (invalid)
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_HVAC_MODE: 'invalid'}) as wrong:
                    self.assertEqual(wrong.status, 400)
                
                # wrong JSON data for settings
                settings = self.timetable.__getstate__()
                settings[timetable.JSON_TEMPERATURES][timetable.JSON_TMAX_STR] = 'inf'
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_ALL: settings}) as wrong:
                    self.assertEqual(wrong.status, 400)
                
                # invalid JSON syntax for settings
                settings = self.timetable.settings()
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_ALL: settings[0:30]}) as wrong:
                    self.assertEqual(wrong.status, 400)
                
                # check original paramethers
                self.assertAlmostEqual(self.timetable.differential, 0.5, delta=0.01)
                self.assertAlmostEqual(self.timetable.tmax, 21, delta=0.01)
        
        self.loop.run_until_complete(this_test())
    
    
    def test_post_right_messages(self):
        async def this_test():
            async with aiohttp.ClientSession() as session:
                # single settings
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_MODE: timetable.JSON_MODE_OFF}) as p:
                    self.assertEqual(p.status, 200)
                    self.assertEqual(self.timetable.mode, timetable.JSON_MODE_OFF)
                
                # multiple settings
                async with session.post(__url_settings__,
                                        data={socket.REQ_SETTINGS_MODE: timetable.JSON_MODE_TMAX,
                                              socket.REQ_SETTINGS_TMAX: 32.3,
                                              socket.REQ_SETTINGS_HVAC_MODE: common.HVAC_COOLING}) as q:
                    
                    self.assertEqual(q.status, 200)
                    self.assertEqual(self.timetable.mode, timetable.JSON_MODE_TMAX)
                    self.assertAlmostEqual(self.timetable.tmax, 32.3, delta=0.01)
                
                # some days
                old_set = self.timetable.__getstate__()
                friday = old_set[timetable.JSON_TIMETABLE][timetable.json_get_day_name(5)]
                sunday = old_set[timetable.JSON_TIMETABLE][timetable.json_get_day_name(7)]
                
                friday['h12'][0] = 44
                friday['h15'][1] = 45
                sunday['h06'][2] = 46
                sunday['h07'][3] = 47
                
                # all settings
                tt2 = copy.deepcopy(self.timetable)
                tt2.mode = timetable.JSON_MODE_TMAX
                tt2.update('thursday', 4, 1, 36.5)
                
                self.assertNotEqual(self.timetable, tt2)  # different before update
                
                async with session.post(__url_settings__, data={socket.REQ_SETTINGS_ALL: tt2.settings()}) as s:
                    self.assertEqual(s.status, 200)
        
                self.assertEqual(self.timetable, tt2)  # equal after update
        
        self.loop.run_until_complete(this_test())
    
    
    def test_unsupported_http_methods(self):
        async def this_test():
            async with aiohttp.ClientSession() as session:
                async with session.patch(__url_settings__, data={}) as pa:
                    self.assertEqual(pa.status, 501)
                
                async with session.put(__url_settings__, data={}) as pu:
                    self.assertEqual(pu.status, 501)
                
                async with session.delete(__url_heating__) as de:
                    self.assertEqual(de.status, 501)
        
        self.loop.run_until_complete(this_test())