Merge pull request #34 from appurify/error_codes

Error codes
diff --git a/appurify/client.py b/appurify/client.py
index fca1080..c6db13a 100644
--- a/appurify/client.py
+++ b/appurify/client.py
@@ -64,6 +64,36 @@
                 raise AppurifyClientError('access_token_generate failed with response %s' % r.text, exit_code=constants.EXIT_CODE_AUTH_FAILURE)
         return self.access_token
 
+    def checkDevice(self):
+        response_device_list = devices_list(self.access_token)
+        data_device_list = json.loads(response_device_list.text.replace("'","\"")) 
+        device_id_list =[]
+        
+        for device in data_device_list["response"]:
+            device_id_list.append( device["device_type_id"])
+
+        if response_device_list.status_code == 200:
+            if int(self.device_type_id) not in device_id_list :
+                raise AppurifyClientError("Current device list does not include device type: %s" % self.device_type_id, exit_code=constants.EXIT_CODE_DEVICE_NOT_FOUND)
+
+    def checkAppCompatibility(self, app_src):
+        response_device_list = devices_list(self.access_token)
+        data_device_list = json.loads(response_device_list.text.replace("'","\"")) 
+        reservingDevice = -1
+        
+        for device in data_device_list["response"]:
+            if int(self.device_type_id) == device["device_type_id"]:
+                reservingDevice = device
+
+        #verify app type works with OS type of device
+        if int(response_device_list.status_code) == 200 and reservingDevice != -1:
+            devicePlatform = reservingDevice["os_name"].lower()
+            appType = app_src[-3:]
+            if (appType == "ipa" and devicePlatform == "android") or (appType == "apk" and devicePlatform == "ios"):
+                 raise AppurifyClientError("Must install .ipa on iOS device or .apk on android device.  Mismatch: %s installing onto an %s device." % (appType, devicePlatform), exit_code=constants.EXIT_CODE_APP_INCOMPATIBLE)
+        
+        
+
     def uploadApp(self):
         log('uploading app file...')
         app_src_type = self.args.get('app_src_type', None)
@@ -79,6 +109,7 @@
             if app_src is None:
                 raise AppurifyClientError("app src is required for test type %s" % self.test_type, exit_code=constants.EXIT_CODE_BAD_TEST)
             if app_src_type != 'url':
+                self.checkAppCompatibility(app_src)
                 with open(app_src, 'rb') as app_file_source:
                     r = apps_upload(self.access_token, app_file_source, app_src_type, app_src_type, app_name)
             else:
@@ -238,15 +269,21 @@
         return exit_code
 
     def getExceptionExitCode(self, test_response):
-        exit_code = constants.EXIT_CODE_OTHER_EXCEPTION
-        for response in test_response:
-            exception = response.get("exception", False)
-            if exception:
-                exception_code = exception.split(":")[0]
-                for key in constants.EXIT_CODE_EXCEPTION_MAP:
-                    if int(exception_code) in constants.EXIT_CODE_EXCEPTION_MAP[key]:
-                        return key
-        return exit_code
+        exit_code = constants.EXIT_CODE_CLIENT_EXCEPTION
+        try:
+            for response in test_response:
+                exception = response.get("exception", False)
+                if exception:
+                    exception_code = exception.split(":")[0]
+                    for key in constants.EXIT_CODE_EXCEPTION_MAP:
+                        try:
+                            #if exception code cannot pasre into int, means server didn't send correctly.
+                            if int(exception_code) in constants.EXIT_CODE_EXCEPTION_MAP[key]:
+                                return key
+                        except Exception: 
+                            exit_code = constants.EXIT_CODE_OTHER_EXCEPTION
+        finally:
+            return exit_code
 
     @staticmethod
     def print_single_test_response(test_response):
@@ -300,6 +337,8 @@
             if self.test_type is None:
                 raise AppurifyClientError("test_type is required")
 
+            self.checkDevice()
+            
             # upload app/test of use passed id's
             app_id = self.args.get('app_id', None) or self.uploadApp()
             test_id = self.args.get('test_id', None) or self.uploadTest(app_id)
@@ -307,6 +346,7 @@
             config_src = self.args.get('config_src', False)
             if config_src:
                 self.uploadConfig(test_id, config_src)
+
             
             # start test run
             test_run_id, queue_timeout_limit, configs = self.runTest(app_id, test_id)
diff --git a/appurify/constants.py b/appurify/constants.py
index 48617aa..87183f2 100644
--- a/appurify/constants.py
+++ b/appurify/constants.py
@@ -11,7 +11,7 @@
 # Current development version
 # Increment this during development as and when desired
 # setup.py will use this version to generate new releases
-VERSION = (0, 4, 3)
+VERSION = (0, 4, 4)
 __version__ = '.'.join(map(str, VERSION[0:3])) + ''.join(VERSION[3:])
 
 # Last tagged stable version
@@ -42,26 +42,36 @@
 API_WAIT_FOR_SERVICE = 1        # should client wait for service to come back live by polling aws status page?
 API_STATUS_BASE_URL = 'https://s3-us-west-1.amazonaws.com/appurify-api-status'
 
-MAX_DOWNLOAD_RETRIES = 10       # Number of times client should try to download the test results before giving up
+MAX_DOWNLOAD_RETRIES = 10           # Number of times client should try to download the test results before giving up
 
 # Exit codes
-EXIT_CODE_ALL_PASS = 0          # Test completed with no exceptions or errors
-EXIT_CODE_TEST_FAILURE = 1      # Test completed normally but reported test failures
-EXIT_CODE_TEST_ABORT = 2        # Test was aborted by the user or system
-EXIT_CODE_TEST_TIMEOUT = 3      # Test was aborted by the system because of timeout
-EXIT_CODE_DEVICE_FAILURE = 4    # Test could not be completed because the device could not be activated or reserved
-EXIT_CODE_BAD_TEST = 5          # Test could not execute because there was an error in the configuration or uploaded files
-EXIT_CODE_AUTH_FAILURE = 6      # Test could not execute because the server rejected the provided credentials (key/secret)
-EXIT_CODE_OTHER_EXCEPTION = 7   # Test could not execute because of other server/remote exception
-EXIT_CODE_CLIENT_EXCEPTION = 8  # Test could not execute because of an unexpected error in the client
-EXIT_CODE_CONNECTION_ERROR = 9  # Test got a connection error attempting to reach the server
-EXIT_CODE_APP_INSTALL_FAILED = 10 # The app could not be installed on the device (possibly due to incorrect build)
+EXIT_CODE_ALL_PASS = 0              # Test completed with no exceptions or errors
+EXIT_CODE_TEST_FAILURE = 1          # Test completed normally but reported test failures
+EXIT_CODE_TEST_ABORT = 2            # Test was aborted by the user or system
+EXIT_CODE_TEST_TIMEOUT = 3          # Test was aborted by the system because of timeout
+EXIT_CODE_DEVICE_FAILURE = 4        # Test could not be completed because the device could not be activated or reserved
+EXIT_CODE_BAD_TEST = 5              # Test could not execute because there was an error in the configuration or uploaded files
+EXIT_CODE_AUTH_FAILURE = 6          # Test could not execute because the server rejected the provided credentials (key/secret)
+EXIT_CODE_OTHER_EXCEPTION = 7       # Test could not execute because of other server/remote exception
+EXIT_CODE_CLIENT_EXCEPTION = 8      # Test could not execute because of an unexpected error in the client
+EXIT_CODE_CONNECTION_ERROR = 9      # Test got a connection error attempting to reach the server
+EXIT_CODE_APP_INSTALL_FAILED = 10   # The app could not be installed on the device (possibly due to incorrect build)
+EXIT_CODE_INVALID_PROVISION = 11    # Test could not execute because device type is not found in user pool 
+EXIT_CODE_INVALID_DEVICE = 12       # Test could not execute because app is not built for device type
+EXIT_CODE_DEVICE_NOT_FOUND = 13     # Device doesn't exist in users device pool
+EXIT_CODE_APP_INCOMPATIBLE = 14     # Device doesn't exist in users device pool
+EXIT_CODE_GRID_TIMEOUT = 15         # Test reached timeout for grid session 
 
 # TODO: Probably should be fetching these from the server at some point
 EXIT_CODE_EXCEPTION_MAP = {EXIT_CODE_TEST_ABORT : [4000, 5000],
+                          EXIT_CODE_APP_INSTALL_FAILED : [4007],
+                          EXIT_CODE_GRID_TIMEOUT : [7003, 7005, 7007],
+                          EXIT_CODE_INVALID_PROVISION : [4008, 4005],
+                          EXIT_CODE_INVALID_DEVICE : [4006, 4009],
                           EXIT_CODE_TEST_TIMEOUT : [4001, 4002, 4003, 1010],
                           EXIT_CODE_DEVICE_FAILURE: [1000, 1001, 1002, 1005, 1008, 1009, 1011, 1012, 1013, 1014],
-                          EXIT_CODE_BAD_TEST: [1003, 1006, 1007, 1008, 3000, 3001, 3003, 3004, 4005, 4006, 4007, 4008, 4009]}
+                          EXIT_CODE_BAD_TEST: [1003, 1006, 1007, 1008, 3000, 3001, 3003, 3004]}
+
 
 SUPPORTED_TEST_TYPES = [
     'calabash',
diff --git a/dist/appurify-0.4.4.tar.gz b/dist/appurify-0.4.4.tar.gz
new file mode 100644
index 0000000..da9c29f
--- /dev/null
+++ b/dist/appurify-0.4.4.tar.gz
Binary files differ