Repository: Phype/purei9_unofficial (language: python)

Issue:
Add some tests
Not sure how to do right now, but some tests would be great.


Discussion / hints:

Fix patch:
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f6b8181
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+.PHONY: test build upload clean
+
+all:
+	@echo "usage: make [test | build | upload | clean]"
+
+test:
+	nosetests -v --nologcapture
+
+build:
+	python3 -m pip install --upgrade build
+	python3 -m build
+
+upload:
+	python3 -m twine upload dist/*
+
+clean:
+	rm dist/*
diff --git a/build_and_upload.sh b/build_and_upload.sh
deleted file mode 100644
index 82478e2..0000000
--- a/build_and_upload.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-python3 -m pip install --upgrade build
-python3 -m build
-python3 -m twine upload dist/*
diff --git a/src/purei9_unofficial/cloud.py b/src/purei9_unofficial/cloud.py
index 818b2ef..329d398 100644
--- a/src/purei9_unofficial/cloud.py
+++ b/src/purei9_unofficial/cloud.py
@@ -37,28 +37,35 @@ def do_http(method, url, retries=2, **kwargs):
             logger.error("Giving up due to no left retries. Wrong credentials?")
             raise r
         
-def cached_data(func, maxage=5):
-    @functools.wraps(func)
-    def self(*args, **kwargs):
-        if self.data != None and time.time() - self.date < self.maxage:
-            return self.data
+class CachedData():
+    
+    def __init__(self, maxage=5):
+        self._mark_changed()
+        self._cache_maxage = maxage
+
+    def _mark_changed(self):
+        self._cache_data = None
+        self._cache_time = time.time()
+        
+    def _getinfo(self):
+        if self._cache_data != None and time.time() - self._cache_time < self._cache_maxage:
+            return self._cache_data
         else:
-            self.data = func(*args, **kwargs)
-            self.date = time.time()
-            return self.data
-    self.data = None
-    self.date = time.time()
-    self.maxage = maxage
-    return self
+            self._cache_data = self._getinfo_inner()
+            self._cache_time = time.time()
+            return self._cache_data
+        
+###
 
-class CloudRobot(AbstractRobot):
+class CloudRobot(AbstractRobot, CachedData):
     
     def __init__(self, cloudclient, id):
+        CachedData.__init__(self)
+        
         self.cloudclient = cloudclient
         self.id          = id
     
-    @cached_data
-    def _getinfo(self):
+    def _getinfo_inner(self):
         r = do_http("POST", self.cloudclient.apiurl + "/robots/AppUpdate", auth=self.cloudclient.httpauth, json={"RobotID": self.id, "Email": self.cloudclient.credentials["Email"], "AccountPassword": self.cloudclient.credentials["AccountPassword"]})
         return r.json()
         
@@ -112,7 +119,7 @@ def startclean(self):
     
     def gohome(self):
         return self._sendCleanCommand(3)
-    
+
     def pauseclean(self):
         return self._sendCleanCommand(4)
     
@@ -198,6 +205,7 @@ def _sendCommand(self, body):
 
             return True
         finally:
+            self._mark_changed()
             ws.close()
         
     def getMaps(self):
@@ -288,14 +296,15 @@ def __init__(self, cloudmap, js):
 
 ###
 
-class CloudRobotv2(AbstractRobot):
+class CloudRobotv2(AbstractRobot, CachedData):
     
     def __init__(self, cloudclient, id):
+        CachedData.__init__(self)
+        
         self.cloudclient = cloudclient
         self.id          = id
     
-    @cached_data
-    def _getinfo(self):
+    def _getinfo_inner(self):
         r = do_http("GET", self.cloudclient.apiurl + "/Appliances/" + self.id, headers=self.cloudclient._getHeaders())
         return r.json()["twin"]
     
@@ -390,6 +399,7 @@ def _sendCleanCommand(self, command):
         # play|stop|home
         # curl -v https://api.delta.electrolux.com/api/Appliances/900277283814002391100106/Commands -X PUT --header "Content-Type: application/json" --header "Authorization: Bearer $TOKEN2" --data "{\"CleaningCommand\":\"home\"}" --http1.1 | jq -C .
         r = do_http("PUT", self.cloudclient.apiurl + "/Appliances/" + self.id + "/Commands", headers=self.cloudclient._getHeaders(), json={"CleaningCommand": command})
+        self._mark_changed()
 
 class CloudClientv2:
     


Test patch:
diff --git a/src/purei9_unofficial/tests/__init__.py b/src/purei9_unofficial/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/purei9_unofficial/tests/test_cloud_v1.py b/src/purei9_unofficial/tests/test_cloud_v1.py
new file mode 100644
index 0000000..1133c70
--- /dev/null
+++ b/src/purei9_unofficial/tests/test_cloud_v1.py
@@ -0,0 +1,76 @@
+import itertools
+import unittest
+import time
+
+from .util import load_secrets
+
+from .. import cloud
+from .. import common
+
+SECRETS  = load_secrets()
+USERNAME = SECRETS["cloud"]["username"]
+PASSWORD = SECRETS["cloud"]["password"]
+ROBOTID  = SECRETS["local"]["robotid"]
+LOCALPW  = SECRETS["local"]["localpassword"]
+
+class TestCloud(unittest.TestCase):
+
+    def test_01(self):
+        cc = cloud.CloudClient(USERNAME, PASSWORD)
+        # self.cc.tryLogin()
+        
+        ###
+        # Robot list
+        ###
+        
+        self.assertIn(ROBOTID, map(lambda rc: rc.getid(), cc.getRobots()), "Robot not found in cloud list")
+        rc = cc.getRobot(ROBOTID)
+        
+        ###
+        # Basic Stuff
+        ###
+        
+        rc.getid()
+        rc.getname()
+        rc.getfirmware()
+        rc.getbattery()
+        
+        ###
+        # Sessions
+        ###
+        
+        for session in rc.getCleaningSessions():
+            self.assertIn("image", session)
+        
+        ###
+        # Maps
+        ###
+        
+        for m in rc.getMaps():
+            m.getImage()
+            
+            # Only testing the first one
+            break
+        
+        ###
+        # PowerMode
+        ###
+        
+        oldmode = rc.getpowermode()
+        
+        toset = common.PowerMode.HIGH
+        if oldmode == common.PowerMode.HIGH:
+            toset = common.PowerMode.MEDIUM
+        
+        rc.setpowermode(toset)
+        time.sleep(0.5)
+        
+        isset = rc.getpowermode()
+        
+        time.sleep(0.5)
+        rc.setpowermode(oldmode)
+        
+        self.assertEqual(isset, toset, "Powermode did not change")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/purei9_unofficial/tests/test_cloud_v2.py b/src/purei9_unofficial/tests/test_cloud_v2.py
new file mode 100644
index 0000000..65e340d
--- /dev/null
+++ b/src/purei9_unofficial/tests/test_cloud_v2.py
@@ -0,0 +1,48 @@
+import itertools
+import unittest
+import time
+
+from .util import load_secrets
+
+from .. import cloud
+from .. import common
+
+SECRETS  = load_secrets()
+USERNAME = SECRETS["cloud"]["username"]
+PASSWORD = SECRETS["cloud"]["password"]
+ROBOTID  = SECRETS["local"]["robotid"]
+LOCALPW  = SECRETS["local"]["localpassword"]
+
+class TestCloud(unittest.TestCase):
+
+    def test_01(self):
+        cc = cloud.CloudClientv2(USERNAME, PASSWORD)
+        cc.tryLogin()
+        
+        ###
+        # Token generation
+        ###
+        
+        token = cc.gettoken()
+        
+        cc2 = cloud.CloudClientv2(token=token)
+        cc2.tryLogin()
+        
+        ###
+        # Robot list
+        ###
+        
+        self.assertIn(ROBOTID, map(lambda rc: rc.getid(), cc.getRobots()), "Robot not found in cloud list")
+        rc = cc.getRobot(ROBOTID)
+        
+        ###
+        # Basic Stuff
+        ###
+        
+        rc.getid()
+        rc.getname()
+        rc.getfirmware()
+        rc.getbattery()
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/purei9_unofficial/tests/test_local.py b/src/purei9_unofficial/tests/test_local.py
new file mode 100644
index 0000000..09ff5bc
--- /dev/null
+++ b/src/purei9_unofficial/tests/test_local.py
@@ -0,0 +1,75 @@
+import itertools
+import unittest
+import time
+
+from .util import load_secrets
+
+from .. import local
+from .. import common
+
+SECRETS_LOCAL = load_secrets()["local"]
+ADDRESS = SECRETS_LOCAL["address"]
+LOCALPW = SECRETS_LOCAL["localpassword"]
+ROBOTID = SECRETS_LOCAL["robotid"]
+
+class TestLocal(unittest.TestCase):
+
+    def test_find(self):
+        for r in itertools.chain(local.find_robots(), local.find_robots(), local.find_robots()):
+            if r.address == ADDRESS:
+                return
+        
+        raise Exception("Did not find expected robot at " + ADDRESS)
+
+    def test_connect_wrongpw(self):
+        rc = local.RobotClient(ADDRESS)
+        
+        msg = ""
+        
+        try:
+            rc.connect("0000000")
+        except Exception as e:
+            msg = str(e)
+            
+        self.assertIn("Bad localpw", msg)
+
+    def test_connect_status(self):
+        rc = local.RobotClient(ADDRESS)
+        rc.connect(LOCALPW)
+        
+        self.assertEqual(rc.getid(), ROBOTID)
+        
+        rc.getstatus()
+        rc.getbattery()
+        rc.getfirmware()
+        
+        rc.disconnect()
+        
+    def test_set_powermode(self):
+        
+        rc = local.RobotClient(ADDRESS)
+        rc.connect(LOCALPW)
+        
+        oldmode = rc.getpowermode()
+        
+        toset = common.PowerMode.HIGH
+        if oldmode == common.PowerMode.HIGH:
+            toset = common.PowerMode.MEDIUM
+            
+        rc.setpowermode(toset)
+        
+        rc.disconnect()
+        time.sleep(0.5)
+        
+        rc = local.RobotClient(ADDRESS)
+        rc.connect(LOCALPW)
+        
+        isset = rc.getpowermode()
+        rc.setpowermode(oldmode)
+        
+        rc.disconnect()
+        
+        self.assertEqual(isset, toset, "Powermode did not change")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/purei9_unofficial/tests/util.py b/src/purei9_unofficial/tests/util.py
new file mode 100644
index 0000000..e59860d
--- /dev/null
+++ b/src/purei9_unofficial/tests/util.py
@@ -0,0 +1,10 @@
+import os
+import json
+
+def load_secrets():
+	filename = os.getenv("HOME") + "/.config/purei9_unofficial/test_secrets.json"
+	try:
+		with open(filename, "r") as fp:
+			return json.loads(fp.read())
+	except:
+		raise Exception("Could not load tests secrets from " + filename)