<NME> tests.js
<BEF> ADDFILE
<MSG> Merge branch 'master' of http://github.com/phonegap/phonegap-plugin-contentsync into Windows

<DFF> @@ -0,0 +1,44 @@
+
+
+exports.defineAutoTests = function() {
+	
+	describe('phonegap-plugin-contentsync', function() {
+        it("should exist", function() {
+            expect(window.ContentSync).toBeDefined();
+            expect(typeof window.ContentSync.sync == 'function').toBe(true);
+            expect(typeof window.ContentSync.download == 'function').toBe(true);
+            expect(typeof window.ContentSync.unzip == 'function').toBe(true);
+        });
+
+        it("can sync", function(done){
+
+        	var progressEvent = null;
+        	var url = "https://github.com/timkim/zipTest/archive/master.zip"; 
+        	var sync = ContentSync.sync({ src: url, id: 'myapps/myapp', type: 'replace', copyCordovaAssets: false, headers: false });
+        
+	        sync.on('progress', function(progress) {
+	            if(!progressEvent) {
+	            	progressEvent = progress;
+	            }
+	        });
+
+	        sync.on('complete', function(data) {
+	        	expect(progressEvent).toBeDefined("Progress should have been received");
+	        	expect(progressEvent.status).toBeDefined("Progress event should have a status prop");
+	        	expect(progressEvent.progress).toBeDefined("Progress event should have a progress prop");
+	        	expect(data).toBeDefined("On complete, data is not null");
+	        	done();
+	        });
+
+	        sync.on('error', function(e) {
+	        	done();
+	        });
+
+        }, 60000); // wait a full 60 secs
+    });
+
+}
+
+exports.defineManualTests = function() {
+
+}

{'commit': 'diff --git a/.travis.yml b/.travis.yml\nindex de1bffc..4c0bea0 100644\n--- a/.travis.yml\n+++ b/.travis.yml\n@@ -1,11 +1,11 @@\n language: node_js\n node_js:\n-  - "0.10"\n+- \'0.10\'\n branches:\n   only:\n-    - master\n+  - master\n notifications:\n   slack:\n-    secure: cO5Xe/87RCs09xWoYQ62xN7BRSqIOQxtaET+8BfRev9v1b9t1h38mb/8vJBqLg548+c2YVqRB+ieUE7IE+jbvdj+Qp+0+yRsKYPTvaZaF5IuAzbbb0GWk/kRykU5ha4SpH5rXAU44NQao+TZlSiJtTp3eLlPHJTDDJfcyZowMvg=\n+    secure: VcJej2eTOwkU9oh/MHXKw/efUMw+o15Fd8yzc8/vR0QVG6ubYaIPlMK9iPTI9+MgkP+DIHxvsVr84XQvM40RSr9M35N21zEJjG/P/He8slmca6MAM9v7V8XEZX9xlptshb276l8DfeJs3sF8YZNnulkqoTBeuXNii5pGUtwG7m0=\n   email:\n-  - PhoneGapCI@adobe.com\n\\ No newline at end of file\n+  - PhoneGapCI@adobe.com\ndiff --git a/README.md b/README.md\nindex f74bda2..37462c0 100644\n--- a/README.md\n+++ b/README.md\n@@ -233,12 +233,24 @@ sync.on(\'complete\', function(data) {\n - The locally compiled Cordova web assets should be copied to the cached content. This includes `cordova.js`, `cordova_plugins.js`, and `plugins/**/*`.\n - Multiple syncs should be supported at the same time.\n \n-## Running Tests\n+## Running Tests ( static tests against source code )\n \n ```\n npm test\n ```\n \n+## Emulator Testing \n+\n+The emulator tests use cordova-paramedic and the cordova-plugin-test-framework.\n+To run them you will need cordova-paramedic installed.\n+\n+    npm install -g cordova-paramedic\n+    // Then from the root of this repo\n+    // test ios :\n+    cordova-paramedic --platform ios --plugin .\n+    // test android :\n+    cordova-paramedic --platform android --plugin .\n+\n ## Contributing\n \n ### Editor Config\ndiff --git a/package.json b/package.json\nindex 472ede4..5aea47f 100644\n--- a/package.json\n+++ b/package.json\n@@ -1,7 +1,7 @@\n {\n     "name": "phonegap-plugin-contentsync",\n     "description": "Fetch and cache content for your PhoneGap app.",\n-    "version": "1.1.6",\n+    "version": "1.1.7",\n     "homepage": "http://github.com/phonegap/phonegap-plugin-contentsync#readme",\n     "repository": {\n         "type": "git",\ndiff --git a/plugin.xml b/plugin.xml\nindex c13acf5..2929078 100644\n--- a/plugin.xml\n+++ b/plugin.xml\n@@ -3,7 +3,7 @@\n <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"\n xmlns:android="http://schemas.android.com/apk/res/android"\n            id="phonegap-plugin-contentsync"\n-      version="1.1.6">\n+      version="1.1.7">\n     <name>content-sync</name>\n     <description>PhoneGap Content Sync Plugin</description>\n     <license></license>\ndiff --git a/spec/index.spec.js b/spec/index.spec.js\nindex 47ec39e..9d5b30b 100644\n--- a/spec/index.spec.js\n+++ b/spec/index.spec.js\n@@ -169,6 +169,23 @@ describe(\'phonegap-plugin-contentsync\', function() {\n                     contentSync.sync(options);\n                 });\n             });\n+            describe(\'options.timeout\', function() {\n+                it(\'should default to 15.0\', function(done) {\n+                    execSpy.andCallFake(function(win, fail, service, id, args) {\n+                        expect(args[6]).toEqual(15.0);\n+                        done();\n+                    });\n+                    contentSync.sync(options);\n+                });\n+                it(\'should be passed as whatever we specify\', function(done) {\n+                    options.timeout = 30.0;\n+                    execSpy.andCallFake(function(win, fail, service, id, args) {\n+                        expect(args[6]).toEqual(options.timeout);\n+                        done();\n+                    });\n+                    contentSync.sync(options);\n+                });\n+            });\n         });\n \n         describe(\'on "progress" event\', function() {\ndiff --git a/src/android/Sync.java b/src/android/Sync.java\nindex 800b494..8ae3dc5 100644\n--- a/src/android/Sync.java\n+++ b/src/android/Sync.java\n@@ -190,10 +190,10 @@ public class Sync extends CordovaPlugin {\n         }\n \n         private int updateBytesRead(int newBytesRead) {\n-          if (newBytesRead != -1) {\n-            bytesRead += newBytesRead;\n-          }\n-          return newBytesRead;\n+            if (newBytesRead != -1) {\n+                bytesRead += newBytesRead;\n+            }\n+            return newBytesRead;\n         }\n \n         @Override\n@@ -209,7 +209,7 @@ public class Sync extends CordovaPlugin {\n         }\n \n         public long getTotalRawBytesRead() {\n-          return bytesRead;\n+            return bytesRead;\n         }\n     }\n \n@@ -435,8 +435,9 @@ public class Sync extends CordovaPlugin {\n                 String type = args.optString(2, TYPE_REPLACE);\n                 Log.d(LOG_TAG, "type = " + type);\n                 File dir = new File(outputDirectory);\n+                Log.d(LOG_TAG, "dir = " + dir.exists());\n \n-                if (type.equals(TYPE_LOCAL)) {\n+                if (type.equals(TYPE_LOCAL) && !dir.exists()) {\n                     if ("null".equals(src) && (copyRootApp || copyCordovaAssets)) {\n                         if (copyRootApp) {\n                             copyRootApp(outputDirectory);\n@@ -445,11 +446,15 @@ public class Sync extends CordovaPlugin {\n                             copyCordovaAssets(outputDirectory);\n                         }\n \n-                    } else if (!dir.exists()) {\n+                    } else {\n                         type = TYPE_REPLACE;\n                     }\n                 }\n \n+                if (!dir.exists()) {\n+                    dir.mkdirs();\n+                }\n+\n                 if (!type.equals(TYPE_LOCAL)) {\n                     // download file\n                     if (download(src, createDownloadFileLocation(id), headers, progress, callbackContext)) {\n@@ -520,6 +525,7 @@ public class Sync extends CordovaPlugin {\n         outputDirectory += outputDirectory.endsWith(File.separator) ? "" : File.separator;\n         outputDirectory += id;\n         Log.d(LOG_TAG, "output dir = " + outputDirectory);\n+\n         return outputDirectory;\n     }\n \n@@ -578,22 +584,32 @@ public class Sync extends CordovaPlugin {\n     }\n \n     private void copyAssetFileOrDir(String outputDirectory, String path, boolean wwwExists) throws IOException {\n-        AssetManager assetManager = cordova.getActivity().getAssets();\n-        String assets[] = null;\n-        assets = assetManager.list(path);\n-        if (assets.length == 0) {\n-            this.copyAssetFile(outputDirectory, path, wwwExists);\n+        if (path.contains(".")) {\n+            try {\n+                this.copyAssetFile(outputDirectory, path, wwwExists);\n+            } catch (IOException e) {\n+                copyAssetDir(outputDirectory, path, wwwExists);\n+            }\n         } else {\n+            copyAssetDir(outputDirectory, path, wwwExists);\n+        }\n+    }\n+\n+    private void copyAssetDir(String outputDirectory, String path, boolean wwwExists) throws IOException {\n+        String assets[] = cordova.getActivity().getAssets().list(path);\n+        if (assets.length != 0) {\n             for (String file : assets) {\n                 copyAssetFileOrDir(outputDirectory, path + File.separator + file, wwwExists);\n             }\n+        } else {\n+            this.copyAssetFile(outputDirectory, path, wwwExists);\n         }\n     }\n \n     private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {\n         String encoding = conn.getContentEncoding();\n         if (encoding != null && encoding.equalsIgnoreCase("gzip")) {\n-          return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));\n+            return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));\n         }\n         return new SimpleTrackingInputStream(conn.getInputStream());\n     }\n@@ -617,9 +633,9 @@ public class Sync extends CordovaPlugin {\n             Method gcMethod  = iccmClass.getMethod("getCookie");\n \n             cookie = (String)gcMethod.invoke(\n-                        iccmClass.cast(\n+                    iccmClass.cast(\n                             gcmMethod.invoke(webView)\n-                        ), target);\n+                    ), target);\n \n             gotCookie = true;\n         } catch (NoSuchMethodException e) {\n@@ -650,7 +666,7 @@ public class Sync extends CordovaPlugin {\n                 }\n             }\n         } catch (JSONException e1) {\n-          // No headers to be manipulated!\n+            // No headers to be manipulated!\n         }\n     }\n \n@@ -684,11 +700,11 @@ public class Sync extends CordovaPlugin {\n         }\n \n         public void checkClientTrusted(X509Certificate[] chain,\n-                String authType) throws CertificateException {\n+                                       String authType) throws CertificateException {\n         }\n \n         public void checkServerTrusted(X509Certificate[] chain,\n-                String authType) throws CertificateException {\n+                                       String authType) throws CertificateException {\n         }\n     } };\n \n@@ -795,8 +811,8 @@ public class Sync extends CordovaPlugin {\n                 }\n \n                 if (ze.isDirectory()) {\n-                   File dir = new File(outputDirectory + compressedName);\n-                   dir.mkdirs();\n+                    File dir = new File(outputDirectory + compressedName);\n+                    dir.mkdirs();\n                 } else {\n                     File file = new File(outputDirectory + compressedName);\n                     file.getParentFile().mkdirs();\n@@ -927,15 +943,15 @@ public class Sync extends CordovaPlugin {\n     private void copyFolder(File src, File dest) throws IOException{\n         if(src.isDirectory()) {\n             if(!dest.exists()){\n-               dest.mkdir();\n+                dest.mkdir();\n             }\n \n             //list all the directory contents\n             String files[] = src.list();\n \n             for (String file : files) {\n-               //recursive copy\n-               copyFolder(new File(src, file), new File(dest, file));\n+                //recursive copy\n+                copyFolder(new File(src, file), new File(dest, file));\n             }\n \n         } else {\n@@ -969,7 +985,7 @@ public class Sync extends CordovaPlugin {\n         int length;\n         //copy the file content in bytes\n         while ((length = in.read(buffer)) > 0){\n-               out.write(buffer, 0, length);\n+            out.write(buffer, 0, length);\n         }\n \n         in.close();\ndiff --git a/src/ios/ContentSync.m b/src/ios/ContentSync.m\nindex 5027f90..aa3386e 100644\n--- a/src/ios/ContentSync.m\n+++ b/src/ios/ContentSync.m\n@@ -45,9 +45,9 @@\n         NSFileManager *fileManager = [NSFileManager defaultManager];\n         NSArray *URLs = [fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask];\n         NSURL *libraryDirectoryUrl = [URLs objectAtIndex:0];\n-        \n+\n         NSURL *appPath = [libraryDirectoryUrl URLByAppendingPathComponent:appId];\n-        \n+\n         if([fileManager fileExistsAtPath:[appPath path]]) {\n             NSLog(@"Found local copy %@", [appPath path]);\n             CDVPluginResult *pluginResult = nil;\n@@ -62,22 +62,22 @@\n         }\n         BOOL copyCordovaAssets = [[command argumentAtIndex:4 withDefault:@(NO)] boolValue];\n         BOOL copyRootApp = [[command argumentAtIndex:5 withDefault:@(NO)] boolValue];\n-        \n+\n         if(copyRootApp == YES || copyCordovaAssets == YES) {\n             CDVPluginResult *pluginResult = nil;\n             NSError* error = nil;\n-            \n+\n             NSLog(@"Creating app directory %@", [appPath path]);\n-            [fileManager createDirectoryAtPath:[appPath path] withIntermediateDirectories:NO attributes:nil error:&error];\n-            \n+            [fileManager createDirectoryAtPath:[appPath path] withIntermediateDirectories:YES attributes:nil error:&error];\n+\n             NSError* errorSetting = nil;\n             BOOL success = [appPath setResourceValue: [NSNumber numberWithBool: YES]\n                                              forKey: NSURLIsExcludedFromBackupKey error: &errorSetting];\n-            \n+\n             if(success == NO) {\n                 NSLog(@"WARNING: %@ might be backed up to iCloud!", [appPath path]);\n             }\n-            \n+\n             if(error != nil) {\n                 pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:LOCAL_ERR];\n                 NSLog(@"%@", [error localizedDescription]);\n@@ -118,12 +118,15 @@\n - (void)startDownload:(CDVInvokedUrlCommand*)command extractArchive:(BOOL)extractArchive {\n \n     CDVPluginResult* pluginResult = nil;\n-    NSString* src = [command.arguments objectAtIndex:0];\n+    NSString* src = [command argumentAtIndex:0 withDefault:nil];\n     NSNumber* timeout = [command argumentAtIndex:6 withDefault:[NSNumber numberWithDouble:15]];\n \n     self.session = [self backgroundSession:timeout];\n \n-    if(src != nil) {\n+    // checking if URL is valid\n+    NSURL *srcURL = [NSURL URLWithString:src];\n+\n+    if(srcURL && srcURL.scheme && srcURL.host) {\n         NSLog(@"startDownload from %@", src);\n         NSURL *downloadURL = [NSURL URLWithString:src];\n \n@@ -162,6 +165,7 @@\n         }\n \n     } else {\n+        NSLog(@"Invalid src URL %@", src);\n         pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:INVALID_URL_ERR];\n     }\n \n@@ -317,7 +321,7 @@\n         NSURL* destinationURL = [NSURL URLWithString:[command argumentAtIndex:1]];\n         NSString* type = [command argumentAtIndex:2 withDefault:@"replace"];\n         BOOL replace = [type isEqualToString:@"replace"];\n-        \n+\n         NSFileManager *fileManager = [NSFileManager defaultManager];\n         if([fileManager fileExistsAtPath:[destinationURL path]] && replace == YES) {\n             NSLog(@"%@ already exists. Deleting it since type is set to `replace`", [destinationURL path]);\n@@ -383,7 +387,7 @@\n         [pluginResult setKeepCallbackAsBool:YES];\n         [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];\n         // END\n-        \n+\n         // Do not BACK UP folder to iCloud\n         NSURL* appURL = [NSURL fileURLWithPath: path];\n         NSError* error = nil;\n@@ -483,15 +487,18 @@\n /**\n  * Should this request be handled by this protocol handler?\n  *\n- * We disable caching on requests using the file:// protocol.\n- * In the future, we may want to limit this or enable it based on configuration.\n+ * We disable caching on requests using the file:// protocol and prefixed with the app\'s Library directory\n+ * In the future, we may want to limit this or enable it based on configuration or not.\n  *\n  * @param theRequest is the inbound NSURLRequest.\n  * @return YES to handle this request with the this NSURLProtocol handler.\n  */\n \n + (BOOL)canInitWithRequest:(NSURLRequest*)theRequest {\n-    return [theRequest.URL.scheme isEqualToString:@"file"];\n+    NSFileManager *fileManager = [NSFileManager defaultManager];\n+    NSArray *URLs = [fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask];\n+    NSURL *libraryDirectoryUrl = [URLs objectAtIndex:0];\n+    return [theRequest.URL.scheme isEqualToString:@"file"] && [theRequest.URL.path hasPrefix:[libraryDirectoryUrl path]];\n }\n \n /**\ndiff --git a/src/wp8/Unzip.cs b/src/wp8/Unzip.cs\nindex 3cad82a..c50fddc 100644\n--- a/src/wp8/Unzip.cs\n+++ b/src/wp8/Unzip.cs\n@@ -115,13 +115,9 @@ namespace WPCordovaClassLib.Cordova.Commands\n                                 {\n                                     if(type == Merge)\n                                     {\n-                                        // only write file if it doesn\'t exist already\n-                                        if (!appStorage.FileExists(destFilePath))\n+                                        using (FileStream outStream = new IsolatedStorageFileStream(destFilePath, FileMode.OpenOrCreate, FileAccess.Write, appStorage))\n                                         {\n-                                            using (FileStream outStream = new IsolatedStorageFileStream(destFilePath, FileMode.OpenOrCreate, FileAccess.Write, appStorage))\n-                                            {\n-                                                WriteStreamToPath(readStream, outStream);\n-                                            }\n+                                            WriteStreamToPath(readStream, outStream);\n                                         }\n                                         FileUnzipProgress progEvt = new FileUnzipProgress(totalFiles, current++);\n                                     }\ndiff --git a/tests/plugin.xml b/tests/plugin.xml\nnew file mode 100644\nindex 0000000..d8b2f18\n--- /dev/null\n+++ b/tests/plugin.xml\n@@ -0,0 +1,11 @@\n+<?xml version="1.0" encoding="UTF-8"?>\n+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"\n+    xmlns:rim="http://www.blackberry.com/ns/widgets"\n+    xmlns:android="http://schemas.android.com/apk/res/android"\n+    id="phonegap-plugin-contentsync-tests"\n+    version="1.0.0">\n+    <name>PhoneGap Content Sync Plugin Tests</name>\n+    <license>Apache 2.0</license>\n+    <js-module src="tests.js" name="tests">\n+    </js-module>\n+</plugin>\ndiff --git a/tests/tests.js b/tests/tests.js\nnew file mode 100644\nindex 0000000..46498bf\n--- /dev/null\n+++ b/tests/tests.js\n@@ -0,0 +1,44 @@\n+\n+\n+exports.defineAutoTests = function() {\n+\t\n+\tdescribe(\'phonegap-plugin-contentsync\', function() {\n+        it("should exist", function() {\n+            expect(window.ContentSync).toBeDefined();\n+            expect(typeof window.ContentSync.sync == \'function\').toBe(true);\n+            expect(typeof window.ContentSync.download == \'function\').toBe(true);\n+            expect(typeof window.ContentSync.unzip == \'function\').toBe(true);\n+        });\n+\n+        it("can sync", function(done){\n+\n+        \tvar progressEvent = null;\n+        \tvar url = "https://github.com/timkim/zipTest/archive/master.zip"; \n+        \tvar sync = ContentSync.sync({ src: url, id: \'myapps/myapp\', type: \'replace\', copyCordovaAssets: false, headers: false });\n+        \n+\t        sync.on(\'progress\', function(progress) {\n+\t            if(!progressEvent) {\n+\t            \tprogressEvent = progress;\n+\t            }\n+\t        });\n+\n+\t        sync.on(\'complete\', function(data) {\n+\t        \texpect(progressEvent).toBeDefined("Progress should have been received");\n+\t        \texpect(progressEvent.status).toBeDefined("Progress event should have a status prop");\n+\t        \texpect(progressEvent.progress).toBeDefined("Progress event should have a progress prop");\n+\t        \texpect(data).toBeDefined("On complete, data is not null");\n+\t        \tdone();\n+\t        });\n+\n+\t        sync.on(\'error\', function(e) {\n+\t        \tdone();\n+\t        });\n+\n+        }, 60000); // wait a full 60 secs\n+    });\n+\n+}\n+\n+exports.defineManualTests = function() {\n+\n+}\n', 'commit_subject': "Merge branch 'master' of http://github.com/phonegap/phonegap-plugin-contentsync into Windows", 'repo_name': 'phonegap/phonegap-plugin-contentsync', 'committer': {'name': 'Jesse MacFadyen', 'email': '989c2025b1e355abaf8e633fd0873ed01375d67f@gmail.com', 'time_sec': '1440029329', 'tz_offset': '-420', 'date': {'seconds': '1440029329'}}, 'commit_tree': '9cc2e0c5719934b396bfae228651c91d5d2d4956', 'commit_message': "Merge branch 'master' of http://github.com/phonegap/phonegap-plugin-contentsync into Windows\n", 'language_name': 'Java', 'license': 'apache-2.0', 'hunks': '@@ -0,0 +1,44 @@\n+\n+\n+exports.defineAutoTests = function() {\n+\t\n+\tdescribe(\'phonegap-plugin-contentsync\', function() {\n+        it("should exist", function() {\n+            expect(window.ContentSync).toBeDefined();\n+            expect(typeof window.ContentSync.sync == \'function\').toBe(true);\n+            expect(typeof window.ContentSync.download == \'function\').toBe(true);\n+            expect(typeof window.ContentSync.unzip == \'function\').toBe(true);\n+        });\n+\n+        it("can sync", function(done){\n+\n+        \tvar progressEvent = null;\n+        \tvar url = "https://github.com/timkim/zipTest/archive/master.zip"; \n+        \tvar sync = ContentSync.sync({ src: url, id: \'myapps/myapp\', type: \'replace\', copyCordovaAssets: false, headers: false });\n+        \n+\t        sync.on(\'progress\', function(progress) {\n+\t            if(!progressEvent) {\n+\t            \tprogressEvent = progress;\n+\t            }\n+\t        });\n+\n+\t        sync.on(\'complete\', function(data) {\n+\t        \texpect(progressEvent).toBeDefined("Progress should have been received");\n+\t        \texpect(progressEvent.status).toBeDefined("Progress event should have a status prop");\n+\t        \texpect(progressEvent.progress).toBeDefined("Progress event should have a progress prop");\n+\t        \texpect(data).toBeDefined("On complete, data is not null");\n+\t        \tdone();\n+\t        });\n+\n+\t        sync.on(\'error\', function(e) {\n+\t        \tdone();\n+\t        });\n+\n+        }, 60000); // wait a full 60 secs\n+    });\n+\n+}\n+\n+exports.defineManualTests = function() {\n+\n+}\n', 'addition_count': 44, 'deletion_count': 0, 'src_file': '/dev/null', 'tgt_file': 'b/tests/tests.js', 'file_extension': '.js', 'before_file': 'ADDFILE', 'message': "Merge branch 'master' of http://github.com/phonegap/phonegap-plugin-contentsync into Windows\n", 'author_name': 'Jesse MacFadyen', 'lang': 'js'}