Commit c945380e authored by Jason Williams's avatar Jason Williams
Browse files

Adds the folder upload method for loose deployment

Also updates the documentation and adds a unit test for this.

Updated the XboxWDPDriver to allow specifying HTTP instead of SMB and also added a register only option.
parent ccda3057
Loading
Loading
Loading
Loading
+128 −61
Original line number Diff line number Diff line
@@ -30,8 +30,10 @@ namespace XboxWdpDriver
        private const string XblInstallUsageMessage = "Usage:\n" +
            "  /appx:<path to Appx> [/depend:<path to dependency1>;<path to dependency2> /cer:<path to certificate>]\n" +
            "        Installs the given AppX package, along with any given dependencies.\n" +
            "  /folder:<path to loose folder> [/depend:<path to dependency1>;<path to dependency2> /cer:<path to certificate>]\n" +
            "        Installs the appx from a loose folder, along with any given dependencies.\n";
            "  /folder:<path to loose folder> [/depend:<path to dependency1>;<path to dependency2> /cer:<path to certificate> /transfer:<SMB or HTTP, SMB is the default> /destfoldername:<folder name, defaults to the same as the loose folder>]\n" +
            "        Installs the appx from a loose folder, along with any given dependencies.\n" +
            "  /register:<subpath on DevelopmentFiles\\LooseFolder to app to register>\n" +
            "        Registers a loose folder that is already present on the device.\n";

        /// <summary>
        /// Event used to indicate that the application install process is complete.
@@ -48,6 +50,20 @@ namespace XboxWdpDriver
        /// </summary>
        private bool verbose;

        /// <summary>
        /// Reference to our portal object.
        /// </summary>
        private DevicePortal portal;

        /// <summary>
        /// Initializes a new instance of the <see cref="InstallOperation"/> class. 
        /// </summary>
        /// <param name="portal">Reference to the device portal object.</param>
        public InstallOperation(DevicePortal portal)
        {
            this.portal = portal;
        }

        /// <summary>
        /// Main entry point for handling an install operation
        /// </summary>
@@ -61,7 +77,7 @@ namespace XboxWdpDriver
                return;
            }

            InstallOperation operation = new InstallOperation();
            InstallOperation operation = new InstallOperation(portal);
            portal.AppInstallStatus += operation.AppInstallStatusHandler;

            if (parameters.HasFlag(ParameterHelper.VerboseFlag))
@@ -81,7 +97,10 @@ namespace XboxWdpDriver

            string appxFile = parameters.GetParameterValue("appx");
            string folderPath = parameters.GetParameterValue("folder");
            string registerPath = parameters.GetParameterValue("register");

            try
            {
                if (!string.IsNullOrEmpty(appxFile))
                {
                    operation.mreAppInstall.Reset();
@@ -98,8 +117,6 @@ namespace XboxWdpDriver
                    }
                }
                else if (!string.IsNullOrEmpty(folderPath))
            {
                try
                {
                    // Install all dependencies one at a time (loose folder doesn't handle dependencies well).
                    foreach (string dependency in dependencies)
@@ -123,13 +140,26 @@ namespace XboxWdpDriver
                        folderPath = folderPath.Remove(folderPath.Length - 1);
                    }

                    string destinationFolderName = parameters.GetParameterValue("destfoldername");

                    if (string.IsNullOrEmpty(destinationFolderName))
                    {
                        // Get just the folder name
                        string folderName = folderPath.Substring(folderPath.LastIndexOf('\\') + 1);

                        destinationFolderName = folderName;
                    }

                    string transferType = parameters.GetParameterValue("transfer");

                    if (string.IsNullOrEmpty(transferType) || string.Equals(transferType, "smb", StringComparison.OrdinalIgnoreCase))
                    {
                        string shareName = Path.Combine("\\\\", parameters.GetParameterValue(ParameterHelper.IpOrHostname), "DevelopmentFiles");
                        string destinationFolder = Path.Combine(shareName, "LooseApps", destinationFolderName);

                        try
                        {
                        operation.CopyDirectory(folderPath, Path.Combine(shareName, "LooseApps", folderName));
                            operation.CopyDirectory(folderPath, destinationFolder);
                        }
                        catch (IOException e)
                        {
@@ -148,7 +178,7 @@ namespace XboxWdpDriver
                                    return;
                                }

                            operation.CopyDirectory(folderPath, Path.Combine(shareName, "LooseApps", folderName));
                                operation.CopyDirectory(folderPath, destinationFolder);

                                NetworkShare.DisconnectFromShare(shareName, false);
                            }
@@ -158,19 +188,44 @@ namespace XboxWdpDriver
                                return;
                            }
                        }
                    }
                    else if (string.Equals(transferType, "http", StringComparison.OrdinalIgnoreCase))
                    {
                        operation.UploadDirectoryOverHttp(folderPath, destinationFolderName);
                    }
                    else
                    {
                        Console.WriteLine(string.Format("Unexpected transfer type received: {0}. Expecting one of SMB or HTTP.", transferType));
                        return;
                    }

                    Task registerTask = portal.RegisterApplication(folderName);
                    Task registerTask = portal.RegisterApplication(destinationFolderName);
                    registerTask.Wait();

                    Console.WriteLine("Install complete.");
                }
                else if (!string.IsNullOrEmpty(registerPath))
                {
                    Task registerTask = portal.RegisterApplication(registerPath);
                    registerTask.Wait();

                    Console.WriteLine("Registration complete.");
                }
                else
                {
                    Console.WriteLine("Must provide an appx package, loose folder, or path to register.");
                    Console.WriteLine();
                    Console.WriteLine(XblInstallUsageMessage);
                    return;
                }
            }
            catch (AggregateException e)
            {
                if (e.InnerException is DevicePortalException)
                {
                    DevicePortalException innerException = e.InnerException as DevicePortalException;

                        Console.WriteLine(string.Format("Exception encountered: 0x{0:X} : {1}", innerException.HResult, innerException.Reason));
                    Console.WriteLine(string.Format("Exception encountered: {0}, hr = 0x{1:X} : {2}", innerException.StatusCode, innerException.HResult, innerException.Reason));
                }
                else if (e.InnerException is OperationCanceledException)
                {
@@ -184,14 +239,6 @@ namespace XboxWdpDriver
                return;
            }
        }
            else
            {
                Console.WriteLine("Must provide an appx package or a loose folder.");
                Console.WriteLine();
                Console.WriteLine(XblInstallUsageMessage);
                return;
            }
        }

        /// <summary>
        /// Cleans up the object's data.
@@ -215,6 +262,26 @@ namespace XboxWdpDriver
            }
        }

        /// <summary>
        /// Recursively uploads a source directory to the console.
        /// </summary>
        /// <param name="folderPath">The source directory.</param>
        /// <param name="relativeDestination">The relative destination directory.</param>
        private void UploadDirectoryOverHttp(string folderPath, string relativeDestination)
        {
            Task uploadTask = this.portal.UploadPackageFolder(folderPath, relativeDestination);
            uploadTask.Wait();

            foreach (string subDir in Directory.GetDirectories(folderPath))
            {
                // Get just the folder name
                string subDirName = subDir.Substring(subDir.LastIndexOf('\\') + 1);
                string destSubDir = Path.Combine(relativeDestination, subDirName);
                
                this.UploadDirectoryOverHttp(subDir, destSubDir);
            }
        }

        /// <summary>
        /// Recursively copies a source directory to a destination directory.
        /// </summary>
+3 −0
Original line number Diff line number Diff line
@@ -236,6 +236,9 @@ namespace XboxWdpDriver
                }
                else if (operation == OperationType.InstallOperation)
                {
                    // Ensure we have an IP since SMB might need it for path generation.
                    parameters.AddParameter(ParameterHelper.IpOrHostname, targetConsole);

                    InstallOperation.HandleOperation(portal, parameters);
                }
                else if (operation == OperationType.RebootOperation)
+16 −2
Original line number Diff line number Diff line
@@ -17,10 +17,10 @@ namespace Microsoft.Tools.WindowsDevicePortal.Tests
    public class XboxAppDeploymentTests : BaseTests
    {
        /// <summary>
        /// First basic test.
        /// Basic test of the register API.
        /// </summary>
        [TestMethod]
        public void XboxAppDeploymentTest()
        public void XboxAppRegisterTest()
        {
            TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.RegisterPackageApi, HttpMethods.Post);

@@ -29,5 +29,19 @@ namespace Microsoft.Tools.WindowsDevicePortal.Tests

            Assert.AreEqual(TaskStatus.RanToCompletion, registerTask.Status);
        }

        /// <summary>
        /// Basic test of the folder upload API.
        /// </summary>
        [TestMethod]
        public void XboxAppUploadFolderTest()
        {
            TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.UploadPackageFolderApi, HttpMethods.Post);

            Task uploadTask = TestHelpers.Portal.UploadPackageFolder("MockData\\XboxOne", "DestinationLooseFolder");
            uploadTask.Wait();

            Assert.AreEqual(TaskStatus.RanToCompletion, uploadTask.Status);
        }
    }
}
+5 −5
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ namespace Microsoft.Tools.WindowsDevicePortal
            this.deviceConnection = connection;
        }

        /// <summary>
        /// Gets or sets handler for reporting connection status.
        /// </summary>
        public event DeviceConnectionStatusEventHandler ConnectionStatus;

        /// <summary>
        /// HTTP Methods
        /// </summary>
@@ -98,11 +103,6 @@ namespace Microsoft.Tools.WindowsDevicePortal
            get { return this.deviceConnection.Connection.Authority; }
        }

        /// <summary>
        /// Gets or sets handler for reporting connection status.
        /// </summary>
        public event DeviceConnectionStatusEventHandler ConnectionStatus;

        /// <summary>
        /// Gets the status code for establishing our connection.
        /// </summary>
+11 −0
Original line number Diff line number Diff line
@@ -90,6 +90,11 @@ namespace Microsoft.Tools.WindowsDevicePortal

                        this.HResult = errorResponse.ErrorCode;
                        this.Reason = errorResponse.ErrorMessage;

                        if (string.IsNullOrEmpty(this.Reason))
                        {
                            this.Reason = errorResponse.Reason;
                        }
                    }
                }
            }
@@ -169,6 +174,12 @@ namespace Microsoft.Tools.WindowsDevicePortal
            /// </summary>
            [DataMember(Name = "ErrorMessage")]
            public string ErrorMessage { get; set; }

            /// <summary>
            /// Gets or sets the Reason
            /// </summary>
            [DataMember(Name = "Reason")]
            public string Reason { get; set; }
        }

        #endregion
Loading