# -*- coding: utf-8 -*-

import os
import time
import shutil
import unittest
import plistlib
import threading
from datetime import datetime, timedelta

"""
Tests for aeios.device
"""

# import modules to test

from aeios import device
from aeios import config

__author__ = 'Sam Forester'
__email__ = 'sam.forester@utah.edu'
__copyright__ = 'Copyright (c) 2019 University of Utah, Marriott Library'
__license__ = 'MIT'
__version__ = "1.1.0"

# location for temporary files created with tests
LOCATION = os.path.join(os.path.dirname(__file__))
TMPDIR = os.path.join(LOCATION, 'tmp', 'device')

def setUpModule():
    """
    create tmp directory
    """
    try:
        os.makedirs(TMPDIR)
    except OSError as e:
        if e.errno != 17:
            # raise Exception unless TMPDIR already exists
            raise
    
def tearDownModule():
    """
    remove tmp directory
    """
    shutil.rmtree(TMPDIR)


class TestNewDeviceInit(unittest.TestCase):

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'new')
        self.ecid = '0x123456789ABCD0'
        self.udid = 'a0111222333444555666777888999abcdefabcde'
        self.name = 'test-ipad-1'
        self.devicetype = 'iPad7,5'
        self.data = {'locationID': '0x00000001',
                     'UDID': self.udid,
                     'ECID': self.ecid,
                     "name": self.name,
                     "deviceType": self.devicetype}
        self.file = os.path.join(self.dir, "{0}.plist".format(self.ecid))
        # self.device = device.Device(self.ecid, self.data, path=self.dir)

    def tearDown(self):
        """
        remove the device record after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
    
    def test_minimal_init_succeeds(self):
        data = {'UDID': self.udid,
                'ECID': self.ecid, 
                'deviceType': self.devicetype}
        d = device.Device(self.ecid, info=data, path=self.dir)

    def test_alternative_minimal_init_fails_missing_ecid(self):
        data = {'UDID': self.udid, 
                'deviceType': self.devicetype}
        with self.assertRaises(device.DeviceError):
            device.Device(None, info=data, path=self.dir)

    def test_init_fails_missing_info(self):
        with self.assertRaises(device.DeviceError):
            d = device.Device(self.ecid, path=self.dir)

    def test_minimal_init_fails_missing_udid(self):
        data = {'deviceType': self.devicetype}
        with self.assertRaises(device.DeviceError):
            d = device.Device(self.ecid, info=data, path=self.dir)

    def test_minimal_init_fails_missing_deviceType(self):
        data = {'ECID': self.ecid}
        with self.assertRaises(device.DeviceError):
            d = device.Device(self.ecid, info=data, path=self.dir)

    def test_device_record_created(self):
        data = {'UDID': self.udid,
                'ECID': self.ecid, 
                'deviceType': self.devicetype}
        d = device.Device(self.ecid, info=data, path=self.dir)
        self.assertTrue(os.path.exists(d.file))

    def test_device_record_has_keys(self):
        data = {'UDID': self.udid,
                'ECID': self.ecid, 
                'deviceType': self.devicetype}
        d = device.Device(self.ecid, info=data, path=self.dir)
        for key, value in plistlib.readPlist(d.file).items():
            expected = self.data.get(key)
            self.assertEquals(expected, value)

    def test_device_record_has_all_keys(self):
        d = device.Device(self.ecid, info=self.data, path=self.dir)
        result = plistlib.readPlist(d.file)
        self.assertEquals(result, self.data)

    def test_device_record_no_extra_keys(self):
        d = device.Device(self.ecid, info=self.data, path=self.dir)
        result = plistlib.readPlist(d.file)
        for k in result.keys():
            self.assertIsNotNone(self.data.get(k))


class TestExistingDeviceInit(unittest.TestCase):

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'existing')
        self.orig = {'ECID': '0x1D481C2E300026',
                     'UDID': 'fbe61f791f298c66ebb00a282f5b070c6cb9dc47',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'deviceName': 'test-student-checkout-ipad-2',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x14100000'}
        self.ecid = self.orig.get('ECID')
        self.device = device.Device(self.ecid, info=self.orig, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_existing_record_initialized(self):
        device.Device(self.ecid, path=self.dir)

    def test_existing_record_no_info(self):
        d = device.Device(self.ecid, path=self.dir)
        for key,value in plistlib.readPlist(d.file).items():
            self.assertEquals(self.orig[key], value)

    def test_existing_record_updated(self):
        new = {'deviceName':'iPad', 'firmwareVersion':'12.0',
               'locationID':'0x14100001', 'buildVersion': '15E303'}
        d = device.Device(self.ecid, info=new, path=self.dir)
        
        result = plistlib.readPlist(d.file)
        for key,value in result.items():
            old = self.orig.get(key)
            expected = new.get(key, old)
            self.assertEquals(expected, value)

    def test_existing_record_updated2(self):
        new = {'deviceName':'iPad', 'firmwareVersion':'12.0',
               'locationID':'0x14100001', 'buildVersion': '15E303'}
        d = device.Device(self.ecid, info=new, path=self.dir)
        
        result = plistlib.readPlist(d.file)
        for k in new.keys():
            self.assertNotEqual(result[k], self.orig[k])

    def test_verify_mismatching_deviceType(self):
        mismatch = {'deviceType': 'mismatch'}
        with self.assertRaises(device.DeviceError):
            device.Device(self.ecid, info=mismatch, path=self.dir)

    def test_verify_mismatching_ECID(self):
        mismatch = {'ECID': 'mismatch'}
        with self.assertRaises(device.DeviceError):
            device.Device(self.ecid, info=mismatch, path=self.dir)


class TestDeviceState(unittest.TestCase):

    file = None

    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'state')
        now = datetime.now()
        self.orig = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'apps': ['app1', 'app2', 'app3'],
                     'background': 'background.png',
                     'erased': now - timedelta(seconds=3),
                     'enrolled': now - timedelta(seconds=2),
                     'checkout': now - timedelta(seconds=1),
                     'checkin': now,
                     'deviceName': 'checkout-ipad-1',
                     'name': 'checkout-ipad-1',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = self.orig.get('ECID')
        self.device = device.Device(self.ecid, info=self.orig, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_erase_removes_other_keys(self):
        keys = ['enrolled', 'apps', 'background']
        r = self.device.record
        for k in keys:
            self.assertTrue(r.has_key(k))
        self.device.erased = datetime.now()
        r = self.device.record
        for k in keys:
            self.assertFalse(r.has_key(k))
    
    def test_checkout(self):
        now = datetime.now().replace(microsecond=0)
        self.device.checkout = now
        self.assertEquals(self.device.checkout, now)

    def test_checkin(self):
        now = datetime.now().replace(microsecond=0)
        self.device.checkin = now
        self.assertEquals(self.device.checkin, now)

    def test_erased(self):
        now = datetime.now().replace(microsecond=0)
        self.device.erased = now
        self.assertEquals(self.device.erased, now)

    def test_enrolled(self):
        now = datetime.now().replace(microsecond=0)
        self.device.enrolled = now
        self.assertEquals(self.device.enrolled, now)


class TestDeviceName(unittest.TestCase):

    file = None

    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'name')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'deviceName': 'checkout-ipad-1',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = self.data.get('ECID')
        self.device = device.Device(self.ecid, info=self.data, 
                                    path=self.dir)
        self.device._testing = True
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_get_name(self):
        expected = self.data['deviceName']
        self.assertEquals(self.device.name, expected)

    def test_set_name(self):
        self.device.name = 'test'
        result = self.device.record
        self.assertEquals(self.device.name, result['name'])

    def test_getting_name_sets_name(self):
        expected = self.data['deviceName']
        name = self.device.name
        result = self.device.record
        self.assertEquals(result['name'], expected)

    def test_default_name_missing(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            name = result['name']

    def test_new_device_name_does_not_affect_name(self):
        name = self.device.name
        _info = {'deviceName': 'iPad'}
        d = device.Device(self.ecid, info=_info, path=self.dir)
        result = self.device.record
        self.assertNotEqual(result['name'], result['deviceName'])


class TestDeviceRestarting(unittest.TestCase):

    file = None

    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'restarting')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'deviceName': 'checkout-ipad-1',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = 'a0111222333444555666777888999abcdefabcde'
        self.device = device.Device(self.ecid, info=self.data, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_restarting_empty_by_default(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['restarting']

    def test_restarting_empty_returns_false(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['restarting']
        self.assertFalse(self.device.restarting)

    def test_restarting_sets_default(self):
        restarting = self.device.restarting
        result = self.device.record
        self.assertFalse(result['restarting'])

    def test_set_restarting(self):
        self.device.restarting = True
        result = self.device.record
        self.assertTrue(result['restarting'])

    def test_set_restarting_adds_timestamp(self):
        self.device.restarting = True
        result = self.device.record
        self.assertTrue(result.get('restarted'))

    def test_restarting_timesout(self):
        self.device.restarting = True
        _restarted = datetime.now() - timedelta(minutes=6)
        self.device.config.update({'restarted':_restarted})
        self.assertFalse(self.device.restarting)


class TestDeviceEnrolled(unittest.TestCase):

    file = None

    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'enrolled')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'deviceName': 'checkout-ipad-1',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = '0x123456789ABCD0'
        self.device = device.Device(self.ecid, info=self.data, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_enrolled_empty_by_default(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['enrolled']

    def test_enrolled_empty_returns_None(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['enrolled']
        self.assertIsNone(self.device.enrolled)

    def test_set_datetime(self):
        self.device.enrolled = datetime.now()
        result = self.device.record
        self.assertIsNotNone(result['enrolled'])

    def test_set_enrolled_boolean(self):
        with self.assertRaises(TypeError):
            self.device.enrolled = True


class TestDeviceCheckin(unittest.TestCase):

    file = None

    @classmethod
    def tearDownClass(cls):
        """One time cleanup for this TestCase. 
        Skipped if setUpClass raises an Exception
        """
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'checkin')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'deviceName': 'checkout-ipad-1',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = '0x123456789ABCD0'
        self.device = device.Device(self.ecid, info=self.data, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_empty_by_default(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['checkin']

    def test_enrolled_empty_returns_None(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['checkin']
        self.assertIsNone(self.device.checkin)

    def test_set_datetime(self):
        self.device.checkin = datetime.now()
        result = self.device.record
        self.assertIsNotNone(result['checkin'])

    def test_set_boolean(self):
        with self.assertRaises(TypeError):
            self.device.checkin = True


class TestDeviceCheckout(unittest.TestCase):

    file = None

    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'checkin')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'deviceName': 'checkout-ipad-1',
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = '0x123456789ABCD0'
        self.device = device.Device(self.ecid, info=self.data, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_empty_by_default(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['checkout']

    def test_empty_returns_None(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['checkout']
        self.assertIsNone(self.device.checkout)

    def test_set_datetime(self):
        self.device.checkout = datetime.now()
        result = self.device.record
        self.assertIsNotNone(result['checkout'])

    def test_set_boolean(self):
        with self.assertRaises(TypeError):
            self.device.checkout = True

   
class TestDeviceEraseProperty(unittest.TestCase):

    file = None

    @classmethod
    def setUpClass(cls):
        cls.now = datetime.now()

    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'checkin')
        self.now = self.__class__.now
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'apps': ['app1', 'app2', 'app3'],
                     'background': 'background.png',
                     'deviceName': 'checkout-ipad-1',
                     'name': 'checkout-ipad-1',
                     'enrolled': self.now,
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = '0x123456789ABCD0'
        self.device = device.Device(self.ecid, info=self.data, path=self.dir)
        self.device.apps = ['app1', 'app2', 'app3']
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_empty_by_default(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['erased']

    def test_empty_returns_None(self):
        with self.assertRaises(KeyError):
            self.device.record['erased']
        self.assertIsNone(self.device.erased)

    def test_set_datetime(self):
        self.device.erased = datetime.now()
        self.assertIsNotNone(self.device.record['erased'])

    def test_set_None(self):
        self.device.erased = datetime.now()
        self.assertIsNotNone(self.device.record['erased'])
        self.device.erased = None
        with self.assertRaises(KeyError):
            erased = self.device.record['erased']
                
    def test_set_boolean(self):
        with self.assertRaises(TypeError):
            self.device.erased = True

    def test_erase_removes_other_attributes(self):
        self.assertIsNotNone(self.device.background)
        self.assertIsNotNone(self.device.enrolled)
        self.assertEquals(self.data['apps'], self.device.apps)
        self.device.erased = self.now
        self.assertIsNone(self.device.background)
        self.assertIsNone(self.device.enrolled)
        self.assertEquals([], self.device.apps)


class TestDeviceLocked(unittest.TestCase):

    file = None
    @classmethod
    def tearDownClass(cls):
        try:
            os.remove(cls.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'checkin')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'bootedState': 'Booted',
                     'buildVersion': '15E302',
                     'apps': ['app1', 'app2', 'app3'],
                     'background': 'background.png',
                     'deviceName': 'checkout-ipad-1',
                     'name': 'checkout-ipad-1',
                     'enrolled': datetime.now(),
                     'deviceType': 'iPad7,5',
                     'firmwareVersion': '11.3.1',
                     'locationID': '0x00000001'}
        self.ecid = '0x123456789ABCD0'
        self.device = device.Device(self.ecid, info=self.data, 
                                    path=self.dir)
        self.__class__.file = self.file = self.device.file

    def tearDown(self):
        """
        remove the device record file after each run
        """
        try:
            os.remove(self.file)
        except OSError as e:
            if e.errno != 2:
                raise
                
    def test_empty_by_default(self):
        with self.assertRaises(KeyError):
            self.device.record['locked']

    def test_empty_returns_None(self):
        result = self.device.record
        with self.assertRaises(KeyError):
            t = result['locked']
        self.assertIsNone(self.device.locked)

    def test_set_datetime(self):
        self.device.locked = datetime.now()
        result = self.device.record
        self.assertIsNotNone(result['locked'])

    def test_set_None(self):
        self.device.locked = datetime.now()
        self.assertIsNotNone(self.device.record['locked'])
        self.device.locked = None
        with self.assertRaises(KeyError):
            locked = self.device.record['locked']
                

    def test_set_boolean(self):
        with self.assertRaises(TypeError):
            self.device.locked = True


class TestDeviceThreading(unittest.TestCase):
    """
    Tests involving threading
    """
    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'threaded')
        self.data = {'locationID':'0x00000001',
                     'UDID':'a0111222333444555666777888999abcdefabcde',
                     'ECID': '0x123456789ABCD0',
                     "deviceType":"iPad7,5"}
        self.ecid = self.data['ECID']
        self.device = device.Device(self.ecid, info=self.data, path=self.dir)
        self.device2 = device.Device(self.ecid, path=self.dir)
        self.device3 = device.Device(self.ecid, path=self.dir)
        
    def tearDown(self):
        try:
            os.remove(self.device.file)
        except OSError as e:
            if e.errno != 2:
                raise

    def test_threaded_update(self):
        """
        test threaded devices update as expected
        """
        # function for threading
        def threaded_update(d, k, v):
            d.update(k, v)

        t1 = threading.Thread(target=threaded_update, 
                              args=(self.device2, 'name', 'd2'))
        t2 = threading.Thread(target=threaded_update, 
                              args=(self.device3, 'name', 'd3'))
        t1.start()
        time.sleep(0.001)
        t2.start()
        t1.join()
        t2.join()
        self.assertEquals(self.device.name, 'd3')


class TestDeviceLocking(unittest.TestCase):

    def setUp(self):
        cls = self.__class__
        self.dir = os.path.join(TMPDIR, 'locking')
        self.data = {'ECID': '0x123456789ABCD0',
                     'UDID': 'a0111222333444555666777888999abcdefabcde',
                     'deviceType': 'iPad7,5'}
        self.ecid = self.data['ECID']
        self.device = device.Device(self.ecid, info=self.data, path=self.dir)
        self.lock = config.FileLock(self.device.config.lockfile, timeout=1)
        
    def test_device_locked(self):
        with self.lock.acquire():
            with self.assertRaises(device.DeviceError):
                d = device.Device(self.ecid, path=self.dir, timeout=0)


class DeviceListTest(unittest.TestCase):

    def setUp(self):
        self.dir = os.path.join(TMPDIR, 'list')
        self.data = [{'ECID': '0x123456789ABCD0',
                      'UDID': 'a0111222333444555666777888999abcdefabcde',
                      'bootedState': 'Booted',
                      'buildVersion': '15G77',
                      'deviceName': 'test-ipad-1',
                      'deviceType': 'iPad7,5',
                      'firmwareVersion': '11.4.1',
                      'locationID': '0x00000001'},
                     {'ECID': '0x123456789ABCD1',
                      'UDID': 'a1111222333444555666777888999abcdefabcde',
                      'bootedState': 'Booted',
                      'buildVersion': '15G77',
                      'deviceName': 'test-ipad-2',
                      'deviceType': 'iPad7,5',
                      'firmwareVersion': '11.4.1',
                      'locationID': '0x00000002'},
                     {'ECID': '0x123456789ABCD2',
                      'UDID': 'a2111222333444555666777888999abcdefabcde',
                      'bootedState': 'Booted',
                      'buildVersion': '15G77',
                      'deviceName': 'test-ipad-3',
                      'deviceType': 'iPad7,5',
                      'firmwareVersion': '11.4.1',
                      'locationID': '0x00000003'}]
        self.devices = []
        for info in self.data:
            d = device.Device(info['ECID'], info, path=self.dir)
            self.devices.append(d)
        
        self.devicelist = device.DeviceList(self.devices)

    def test_type(self):
        self.assertIsInstance(self.devicelist, device.DeviceList)

    def test_contains_object(self):
        for d in self.devices:
            self.assertTrue(d in self.devicelist)

    def test_contains_identical(self):
        data = self.data[0]
        identical = device.Device(data['ECID'], data, path=self.dir)
        self.assertIsNot(identical, self.devices[0])
        self.assertTrue(identical in self.devicelist)


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