using NUnit.Framework; using OpenTap.Package; using System; using System.IO; using System.Linq; using System.Threading; using OpenTap.Cli; namespace OpenTap.Image.Tests { [Display("DeployProgress", Description: "Test progress update events", "Image")] public class TestDeplopyProgress : ICliAction { private static TraceSource log = Log.CreateSource(nameof(TestDeplopyProgress)); public int Execute(CancellationToken cancellationToken) { var listeners = Log.GetListeners().ToArray(); foreach (var l in listeners) { Log.RemoveListener(l); } var imageSpecifier = ImageSpecifier.FromString("OpenTAP,CSV,Runner,Demonstration"); imageSpecifier.Repositories = PackageManagerSettings.Current.Repositories .Where(x => x.IsEnabled) .Select(x => x.Url) .ToList(); // Install once without cache, and once with cache for (int i = 0; i < 2; i++) { var img = imageSpecifier.Resolve(cancellationToken); img.ProgressUpdate += (percent, message) => { Console.WriteLine($"{message} ({percent}%)"); }; var tmp = Path.Combine(Path.GetTempPath(), $"MyDeployerTarget{i}"); if (Directory.Exists(tmp)) { Directory.Delete(tmp, true); } Directory.CreateDirectory(tmp); img.Deploy(tmp, cancellationToken); } PackageCacheHelper.ClearCache(); return 0; } } /// /// This is a helper for creating temprary OpenTAP installations that will clean up after itself /// internal class TempInstall : IDisposable { internal TempInstall() { _directoryName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); } private string _directoryName; public Installation Installation => new Installation(_directoryName); public string Directory => _directoryName; public void Dispose() { if (System.IO.Directory.Exists(_directoryName)) System.IO.Directory.Delete(_directoryName, true); } } public class Deploy { [Test] public void DeployClean() { using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); var nonSystemWidePackages = tempInstall.Installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(3, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing" && s.Version.ToString().StartsWith("1.0.44"))); } [Test] public void DeployNewVersion() { var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); Installation installation = tempInstall.Installation; var nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(3, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing")); imageSpecifier.Packages.Clear(); imageSpecifier.Packages.Add(new PackageSpecifier("Keysight Floating Licensing", new VersionSpecifier(1, 4, 1, null, null, VersionMatchBehavior.Exact))); imageSpecifier.MergeAndDeploy(installation, CancellationToken.None); installation = tempInstall.Installation; nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(3, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing" && s.Version.ToString().StartsWith("1.4.1"))); } [Test] public void DeployNewPackage() { using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); Installation installation = tempInstall.Installation; var nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(3, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing")); imageSpecifier.Packages.Clear(); imageSpecifier.Packages.Add(new PackageSpecifier("Python", new VersionSpecifier(2, 3, 0, null, null, VersionMatchBehavior.Exact))); imageSpecifier.MergeAndDeploy(installation, CancellationToken.None); installation = tempInstall.Installation; nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(4, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing" && s.Version.ToString().StartsWith("1.0.44"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Python" && s.Version.ToString().StartsWith("2.3.0"))); } [Test] public void DeployNewAndUpgrade() { using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("Python", new VersionSpecifier(2, 3, 0, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); Installation installation = tempInstall.Installation; var nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(2, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.15.2"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Python" && s.Version.ToString().StartsWith("2.3.0"))); imageSpecifier.Packages.Clear(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); imageSpecifier.MergeAndDeploy(installation, CancellationToken.None); installation = tempInstall.Installation; nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(4, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing" && s.Version.ToString().StartsWith("1.0.44"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Python" && s.Version.ToString().StartsWith("2.3.0"))); } [Test] public void DeployNewAndDowngrade() { using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("Python", new VersionSpecifier(2, 3, 0, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); Installation installation = tempInstall.Installation; var nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(2, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.15.2"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Python" && s.Version.ToString().StartsWith("2.3.0"))); imageSpecifier.Packages.Clear(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 5, 0, null, null, VersionMatchBehavior.Exact))); imageSpecifier.MergeAndDeploy(installation, CancellationToken.None); installation = tempInstall.Installation; nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(4, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.5.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing" && s.Version.ToString().StartsWith("1.0.44"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Python" && s.Version.ToString().StartsWith("2.3.0"))); } [Test] public void DeployOverwrite() { using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); Installation installation = tempInstall.Installation; var nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(3, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "REST-API" && s.Version.ToString().StartsWith("2.6.3"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.16.0"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Keysight Floating Licensing")); imageSpecifier.Packages.Clear(); imageSpecifier.Packages.Add(new PackageSpecifier("Python", new VersionSpecifier(2, 3, 0, null, null, VersionMatchBehavior.Exact))); identifier = imageSpecifier.Resolve(CancellationToken.None); identifier.Deploy(tempInstall.Directory, CancellationToken.None); // Overwrite installation = tempInstall.Installation; nonSystemWidePackages = installation.GetPackages().Where(s => s.Class != "system-wide").ToList(); Assert.AreEqual(2, nonSystemWidePackages.Count); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "OpenTAP" && s.Version.ToString().StartsWith("9.15.2"))); Assert.IsTrue(nonSystemWidePackages.Any(s => s.Name == "Python" && s.Version.ToString().StartsWith("2.3.0"))); } [Test] [Ignore("No access to internal repository")] public void DeployOverwriteTransitiveDependencies() { using var tempInstall = new TempInstall(); string problemImage = @" { ""Packages"": [ { ""Name"": ""License Injector"", ""Version"": ""9.8.0-beta.5+6fce512f"" }], ""Repositories"": [ ""https://packages.opentap.keysight.com"", ""https://packages.opentap.io"" ] }"; var openTapSpec = new PackageSpecifier("OpenTAP", VersionSpecifier.Parse("9.16.0")); { // Deploy once var imageSpecifier = new ImageSpecifier(); imageSpecifier.Repositories.Add("https://packages.opentap.io"); imageSpecifier.Packages.Add(openTapSpec); var res = imageSpecifier.MergeAndDeploy(tempInstall.Installation, CancellationToken.None); Assert.AreEqual(1, res.GetPackages().Where(s => s.Class != "system-wide").Count()); Assert.IsTrue(res.FindPackage("OpenTAP").Version.ToString().StartsWith(openTapSpec.Version.ToString())); } { // Deploy twice PackageCacheHelper.ClearCache(); var imageSpecifier = ImageSpecifier.FromString(problemImage); var res = imageSpecifier.MergeAndDeploy(tempInstall.Installation, CancellationToken.None); Assert.AreEqual(5, res.GetPackages().Where(s => s.Class != "system-wide").Count()); Assert.AreEqual(res.FindPackage("Keg").Version.ToString(), "0.1.0-beta.17+cd0310b9"); } } [Test] public void DeployDependencyMissing() { using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("PackageWithMissingDependency", VersionSpecifier.Any)); try { imageSpecifier.MergeAndDeploy(tempInstall.Installation, CancellationToken.None); Assert.Fail("This should fail to deploy."); } catch (ImageResolveException ex) { var result = ex.Result.ToString(); StringAssert.Contains("The following required packages do not exist", result); } } [Test] public void Cache() { PackageCacheHelper.ClearCache(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); Assert.IsFalse(identifier.Cached); identifier.Cache(); Assert.IsTrue(identifier.Cached); } public class CustomPackageActionBomb : ICustomPackageAction { public CustomPackageActionBomb() { if (BombArmed) throw new Exception("Boom"); } public static bool BombArmed = false; public int Order() { return 0; } public PackageActionStage ActionStage => PackageActionStage.Install; public bool Execute(PackageDef package, CustomPackageActionArgs customActionArgs) { return true; } } [Test] public void TestThrowingCustomPackageAction() { try { CustomPackageActionBomb.BombArmed = true; using var tempInstall = new TempInstall(); var imageSpecifier = MockRepository.CreateSpecifier(); imageSpecifier.Packages.Add(new PackageSpecifier("REST-API", new VersionSpecifier(2, 6, 3, null, null, VersionMatchBehavior.Exact))); var identifier = imageSpecifier.Resolve(CancellationToken.None); Assert.DoesNotThrow(() => identifier.Deploy(tempInstall.Directory, CancellationToken.None)); } finally { CustomPackageActionBomb.BombArmed = false; } } } }