private void ModifyState(ISnPatch patch, List <SnComponentDescriptor> installed, ExecutionResult result) { var target = installed.First(x => x.ComponentId == patch.ComponentId); switch (result) { case ExecutionResult.Successful: target.Version = patch.Version; target.TempVersionAfter = patch.Version; target.State = result; break; case ExecutionResult.Faulty: target.TempVersionAfter = patch.Version; target.State = result; break; case ExecutionResult.Unfinished: case ExecutionResult.FaultyBefore: case ExecutionResult.SuccessfulBefore: target.TempVersionBefore = patch.Version; target.State = result; break; default: throw new ArgumentOutOfRangeException(); } }
public void PatchingExecSim_Install_Dependency2() { // Test the right installer execution order if there is a dependency among the installers. // One dependency is exist, 2 will be installed. var installed = new[] { Comp("C1", "v1.0") }; var patches = new ISnPatch[] { Inst("C3", "v1.0", new[] { Dep("C1", "1.0 <= v"), Dep("C2", "1.0 <= v") }, null), Inst("C4", "v1.0", new[] { Dep("C3", "1.0 <= v"), Dep("C2", "1.0 <= v") }, null), Inst("C2", "v1.0", new[] { Dep("C1", "1.0 <= v") }, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual(4, after.Length); Assert.AreEqual("C1,C2,C3,C4", string.Join(",", after.Select(x => x.ComponentId))); Assert.AreEqual(3, executables.Length); Assert.AreEqual("C2,C3,C4", string.Join(",", executables.Select(x => x.ComponentId))); }
public void PatchingExec_InstallOne_Faulty() { var packages = new List <Package[]>(); var log = new List <PatchExecutionLogRecord>(); void LogMessage(PatchExecutionLogRecord record) { packages.Add(LoadPackages()); log.Add(record); } var installed = new SnComponentDescriptor[0]; var patches = new ISnPatch[] { Inst("C1", "v1.0", null, Error), }; // ACTION var context = new PatchExecutionContext(null, LogMessage); var pm = new PatchManager(context); pm.ExecuteRelevantPatches(patches, installed, context); // ASSERT Assert.AreEqual("ErrorInExecution C1: 1.0", ErrorsToString(context)); Assert.AreEqual(3, log.Count); Assert.AreEqual("[C1: 1.0] ExecutionStart.", log[0].ToString()); Assert.AreEqual("[C1: 1.0] ExecutionError. Err", log[1].ToString()); Assert.AreEqual("[C1: 1.0] ExecutionFinished. Faulty", log[2].ToString()); Assert.AreEqual("1, C1: Install Unfinished, 1.0", PackagesToString(packages[0])); Assert.AreEqual("1, C1: Install Unfinished, 1.0", PackagesToString(packages[1])); Assert.AreEqual("1, C1: Install Faulty, 1.0", PackagesToString(packages[2])); }
public void PatchingExecSim_Install_1New1Skip() { var installed = new[] { Comp("C1", "v1.0") }; var patches = new ISnPatch[] { // Will be skipped. The different version does not cause any error because this patch is irrelevant. Inst("C1", "v1.1", null, null), Inst("C2", "v2.3", null, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual(2, after.Length); Assert.AreEqual("C1", after[0].ComponentId); Assert.AreEqual(new Version(1, 0), after[0].Version); Assert.AreEqual("C2", after[1].ComponentId); Assert.AreEqual(new Version(2, 3), after[1].Version); Assert.AreEqual(1, executables.Length); Assert.AreEqual("C2", executables[0].ComponentId); Assert.AreEqual(new Version(2, 3), executables[0].Version); }
public void PatchingExecSim_Install_Dependencies_Circular() { // Test the right installer execution order if there is a dependency among the installers. // One dependency is exist, 2 will be installed. var installed = new[] { Comp("C1", "v1.0") }; var patches = new ISnPatch[] { Inst("C2", "v1.0", new[] { Dep("C3", "1.0 <= v") }, null), Inst("C3", "v1.0", new[] { Dep("C4", "1.0 <= v") }, null), Inst("C4", "v1.0", new[] { Dep("C2", "1.0 <= v") }, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual(1, after.Length); Assert.AreEqual("C1", string.Join(",", after.Select(x => x.ComponentId))); Assert.AreEqual(0, executables.Length); // Circular dependency causes error but the reason is not recognized yet. Assert.AreEqual(3, context.Errors.Count); Assert.AreEqual(PatchExecutionErrorType.CannotInstall, context.Errors[0].ErrorType); Assert.AreEqual(PatchExecutionErrorType.CannotInstall, context.Errors[1].ErrorType); Assert.AreEqual(PatchExecutionErrorType.CannotInstall, context.Errors[2].ErrorType); Assert.AreEqual("C2", context.Errors[0].FaultyPatch.ComponentId); Assert.AreEqual("C3", context.Errors[1].FaultyPatch.ComponentId); Assert.AreEqual("C4", context.Errors[2].FaultyPatch.ComponentId); }
private bool HasCorrectDependencies(ISnPatch patch, List <SnComponentDescriptor> installed, bool onBefore) { // All right if there is no any dependency. if (patch.Dependencies == null) { return(true); } var deps = patch.Dependencies.ToArray(); if (deps.Length == 0) { return(true); } // Not installable if there is any dependency but installed nothing. if (installed.Count == 0) { return(false); } // Installable if all dependencies exist. return(deps.All(dep => installed.Any(c => c.ComponentId == dep.Id && dep.Boundary.ContainsVersion(GetDependencyTargetVersion(c, onBefore))))); }
public PatchExecutionError(PatchExecutionErrorType errorType, ISnPatch faultyPatch, string message) { ErrorType = errorType; FaultyPatch = faultyPatch; FaultyPatches = faultyPatch == null ? new ISnPatch[0] : new[] { faultyPatch }; Message = message; }
private bool CheckSelfDependency(ISnPatch patch) { if (patch.Dependencies == null || patch.Dependencies.All(d => d.Id != patch.ComponentId)) { return(true); } _context.Errors.Add(new PatchExecutionError(PatchExecutionErrorType.SelfDependencyForbidden, patch, "Self dependency is forbidden.")); return(false); }
private string CheckDescription(string text, ISnPatch patch) { if (string.IsNullOrEmpty(text)) { throw new InvalidPatchException(PatchErrorCode.MissingDescription, patch, "The description cannot be null or empty."); } return(text); }
internal static Version ParseVersion(string version, ISnPatch patch) { try { return(Version.Parse(version.TrimStart('v', 'V'))); } catch (Exception e) { throw new InvalidPatchException(PatchErrorCode.InvalidVersion, patch, e.Message, e); } }
private DateTime ParseDate(string date, ISnPatch patch) { try { return(DateTime.Parse(date)); } catch (Exception e) { throw new InvalidPatchException(PatchErrorCode.InvalidDate, patch, e.Message, e); } }
private void CreateInitialState(ISnPatch patch, List <SnComponentDescriptor> installed) { var current = installed.FirstOrDefault(x => x.ComponentId == patch.ComponentId); if (current == null) { installed.Add(new SnComponentDescriptor(patch.ComponentId, null, patch.Description, patch.Dependencies?.ToArray()) { State = ExecutionResult.Unfinished, TempVersionBefore = patch.Version }); } }
public void PatchingExec_SkipPatch_FaultySnPatch() { // Faulty execution blocks the following patches on the same component. var packages = new List <Package[]>(); var log = new List <PatchExecutionLogRecord>(); void LogMessage(PatchExecutionLogRecord record) { packages.Add(LoadPackages()); log.Add(record); } var executed = new List <ISnPatch>(); void Execute(PatchExecutionContext peContext) => executed.Add(peContext.CurrentPatch); var patches = new ISnPatch[] { Patch("C1", "2.0 <= v < 3.0", "v3.0", Execute), Patch("C1", "1.0 <= v < 2.0", "v2.0", Error), Inst("C1", "v1.0", null, Execute), }; // ACTION var context = new PatchExecutionContext(null, LogMessage); var pm = new PatchManager(context); pm.ExecuteRelevantPatches(patches, context); // ASSERT Assert.AreEqual("ErrorInExecution C1: 1.0 <= v < 2.0 --> 2.0, " + "MissingVersion C1: 2.0 <= v < 3.0 --> 3.0", string.Join(", ", context.Errors.Select(x => x.ToString()))); Assert.AreEqual("C1i1.0", PatchesToString(executed.ToArray())); Assert.AreEqual(6, log.Count); Assert.AreEqual("[C1: 1.0] ExecutionStart.", log[0].ToString()); Assert.AreEqual("[C1: 1.0] ExecutionFinished. Successful", log[1].ToString()); Assert.AreEqual("[C1: 1.0 <= v < 2.0 --> 2.0] ExecutionStart.", log[2].ToString()); Assert.AreEqual("[C1: 1.0 <= v < 2.0 --> 2.0] ExecutionError. Err", log[3].ToString()); Assert.AreEqual("[C1: 1.0 <= v < 2.0 --> 2.0] ExecutionFinished. Faulty", log[4].ToString()); Assert.AreEqual("[C1: 2.0 <= v < 3.0 --> 3.0] CannotExecuteMissingVersion.", log[5].ToString()); Assert.AreEqual(6, packages.Count); Assert.AreEqual("1, C1: Install Unfinished, 1.0", PackagesToString(packages[0])); Assert.AreEqual("1, C1: Install Successful, 1.0", PackagesToString(packages[1])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Unfinished, 2.0", PackagesToString(packages[2])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Unfinished, 2.0", PackagesToString(packages[3])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Faulty, 2.0", PackagesToString(packages[4])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Faulty, 2.0", PackagesToString(packages[5])); }
public void PatchingExecSim_Install_1New() { var patches = new ISnPatch[] { Inst("C1", "v1.0", null, null), }; var installed = new SnComponentDescriptor[0]; var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); Assert.AreEqual(1, after.Length); Assert.AreEqual(1, executables.Length); }
public void PatchingExec_PatchOne_Faulty() { var packages = new List <Package[]>(); var log = new List <PatchExecutionLogRecord>(); void LogMessage(PatchExecutionLogRecord record) { packages.Add(LoadPackages()); log.Add(record); } var executed = new List <ISnPatch>(); void Execute(PatchExecutionContext peContext) => executed.Add(peContext.CurrentPatch); var installed = new SnComponentDescriptor[0]; var patches = new ISnPatch[] { Patch("C1", "1.0 <= v < 2.0", "v2.0", null, Error), Inst("C1", "v1.0", null, Execute), }; // ACTION var context = new PatchExecutionContext(null, LogMessage); var pm = new PatchManager(context); pm.ExecuteRelevantPatches(patches, installed, context); // ASSERT Assert.AreEqual("ErrorInExecution C1: 1.0 <= v < 2.0 --> 2.0", ErrorsToString(context)); Assert.AreEqual(5, log.Count); Assert.AreEqual("C1i1.0", PatchesToString(executed.ToArray())); Assert.AreEqual("[C1: 1.0] ExecutionStart.", log[0].ToString()); Assert.AreEqual("[C1: 1.0] ExecutionFinished. Successful", log[1].ToString()); Assert.AreEqual("[C1: 1.0 <= v < 2.0 --> 2.0] ExecutionStart.", log[2].ToString()); Assert.AreEqual("[C1: 1.0 <= v < 2.0 --> 2.0] ExecutionError. Err", log[3].ToString()); Assert.AreEqual("[C1: 1.0 <= v < 2.0 --> 2.0] ExecutionFinished. Faulty", log[4].ToString()); Assert.AreEqual(5, packages.Count); Assert.AreEqual("1, C1: Install Unfinished, 1.0", PackagesToString(packages[0])); Assert.AreEqual("1, C1: Install Successful, 1.0", PackagesToString(packages[1])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Unfinished, 2.0", PackagesToString(packages[2])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Unfinished, 2.0", PackagesToString(packages[3])); Assert.AreEqual("1, C1: Install Successful, 1.0|2, C1: Patch Faulty, 2.0", PackagesToString(packages[4])); }
/// <summary> /// Creates a <c>Manifest</c> instance from the given <paramref name="package"/> /// </summary> /// <param name="patch">Source <c>ISnPatch</c>.</param> /// <returns></returns> public static Manifest Create(ISnPatch patch) { Dependency[] dependencies; if (patch is SnPatch snPatch) { var selfDependency = new Dependency { Id = snPatch.ComponentId, Boundary = snPatch.Boundary }; if (patch.Dependencies == null) { dependencies = new[] { selfDependency }; } else { var list = patch.Dependencies.ToList(); list.Insert(0, selfDependency); dependencies = list.ToArray(); } } else { dependencies = patch.Dependencies?.ToArray(); } var manifest = new Manifest { PackageType = patch.Type, ComponentId = patch.ComponentId, Description = patch.Description, ReleaseDate = patch.ReleaseDate, Dependencies = dependencies, Version = patch.Version, Parameters = new Dictionary <string, string>() }; manifest.SystemInstall = manifest.ComponentId == SystemComponentId && manifest.PackageType == PackageType.Install; manifest.ManifestXml = manifest.ToXml(); return(manifest); }
public void PatchingExecSim_Patch_C2toC1v2_b() { var installed = new SnComponentDescriptor[0]; var patches = new ISnPatch[] { Patch("C1", "1.0 <= v < 2.0", "v2.0", null, null), Inst("C2", "v1.0", new[] { Dep("C1", "2.0 <= v <= 2.0") }, null), Inst("C1", "v1.0", null, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual("C1v2.0 C2v1.0", ComponentsToString(after)); Assert.AreEqual("C1i1.0 C1p2.0 C2i1.0", PatchesToString(executables)); }
public void PatchingExecSim_Patch_MissingItemInTheChain_a() { var installed = new SnComponentDescriptor[0]; var patches = new ISnPatch[] { Patch("C1", "3.0 <= v < 4.0", "v4.0", null, null), Patch("C1", "1.0 <= v < 2.0", "v2.0", null, null), Inst("C1", "v1.0", null, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual("MissingVersion C1: 3.0 <= v < 4.0 --> 4.0", ErrorsToString(context)); Assert.AreEqual("C1v2.0", ComponentsToString(after)); Assert.AreEqual("C1i1.0 C1p2.0", PatchesToString(executables)); }
/* =========================================================================================== */ internal static Package CreatePackage(ISnPatch patch) { var package = new Package { ComponentId = patch.ComponentId, ComponentVersion = patch.Version, Description = patch.Description, ReleaseDate = patch.ReleaseDate, PackageType = patch.Type, ExecutionDate = patch.ExecutionDate, ExecutionResult = patch.ExecutionResult, ExecutionError = patch.ExecutionError }; Dependency[] dependencies; if (patch is SnPatch snPatch) { var selfDependency = new Dependency { Id = snPatch.ComponentId, Boundary = snPatch.Boundary }; if (patch.Dependencies == null) { dependencies = new[] { selfDependency }; } else { var list = patch.Dependencies.ToList(); list.Insert(0, selfDependency); dependencies = list.ToArray(); } } else { dependencies = patch.Dependencies.ToArray(); } package.Manifest = Manifest.Create(package, dependencies, false).ToXmlString(); return(package); }
public void PatchingExecSim_Install_Duplicates() { var installed = new[] { Comp("C1", "v1.0") }; var patches = new ISnPatch[] { // Will be skipped. The same id does not cause any error because this patch is irrelevant. Inst("C1", "v1.0", null, null), Inst("C1", "v1.1", null, null), // Would be executable but the same id causes an error. Inst("C2", "v1.0", null, null), Inst("C3", "v1.0", null, null), Inst("C3", "v2.3", null, null), Inst("C4", "v1.0", null, null), Inst("C4", "v2.3", null, null), Inst("C4", "v1.0", null, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual(1, after.Length); Assert.AreEqual(0, executables.Length); var errors = context.Errors; Assert.AreEqual(2, errors.Count); Assert.IsTrue(errors[0].Message.Contains("C3")); Assert.AreEqual(PatchExecutionErrorType.DuplicatedInstaller, errors[0].ErrorType); Assert.AreEqual(2, errors[0].FaultyPatches.Length); Assert.IsTrue(errors[1].Message.Contains("C4")); Assert.AreEqual(PatchExecutionErrorType.DuplicatedInstaller, errors[1].ErrorType); Assert.AreEqual(3, errors[1].FaultyPatches.Length); }
public void PatchingExecSim_Patch_MissingItemInTheChain_d() { var installed = new[] { Comp("C1", "v3.0") }; var patches = new ISnPatch[] { Patch("C1", "3.0 <= v < 4.0", "v4.0", null, null), Patch("C1", "1.0 <= v < 2.0", "v2.0", null, null), Inst("C1", "v1.0", null, null), }; // ACTION var context = new PatchExecutionContext(null, null); var pm = new PatchManager(context); var executables = pm.GetExecutablePatches(patches, installed, context, out var after).ToArray(); // ASSERT Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual("C1v4.0", ComponentsToString(after)); Assert.AreEqual("C1p4.0", PatchesToString(executables)); }
public void PatchingExec_InstallOne_Success() { var packages = new List <Package[]>(); var log = new List <PatchExecutionLogRecord>(); void LogMessage(PatchExecutionLogRecord record) { packages.Add(LoadPackages()); log.Add(record); } var executed = new List <ISnPatch>(); void Execute(PatchExecutionContext peContext) => executed.Add(peContext.CurrentPatch); var installed = new SnComponentDescriptor[0]; var patches = new ISnPatch[] { Inst("C1", "v1.0", null, Execute), }; // ACTION var context = new PatchExecutionContext(null, LogMessage); var pm = new PatchManager(context); pm.ExecuteRelevantPatches(patches, installed, context); // ASSERT Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(2, log.Count); Assert.AreEqual("C1i1.0", PatchesToString(executed.ToArray())); Assert.AreEqual("[C1: 1.0] ExecutionStart.", log[0].ToString()); Assert.AreEqual("[C1: 1.0] ExecutionFinished. Successful", log[1].ToString()); Assert.AreEqual("1, C1: Install Unfinished, 1.0", PackagesToString(packages[0])); Assert.AreEqual("1, C1: Install Successful, 1.0", PackagesToString(packages[1])); }
private void ModifyStateInDb(ISnPatch patch, ExecutionResult result, Exception exception) { PackageManager.SavePackage(Manifest.Create(patch), result, exception); }
private VersionBoundary ParseFromVersion(string src, ISnPatch patch) { return(new VersionBoundary { MinVersion = ParseVersion(src, patch) }); }
/* ======================================================================= TOOLS */ private void SavePackage(ISnPatch patch, ExecutionResult result, bool insertOnly = false) { PackageManager.SavePackage(Manifest.Create(patch), result, null, insertOnly); }
/* ======================================================================= TOOLS */ private void SavePackage(ISnPatch patch, ExecutionResult result) { PackageManager.SavePackage(Manifest.Create(patch), result, null); }
public PatchExecutionLogRecord(PatchExecutionEventType eventType, ISnPatch patch, string message = null) { EventType = eventType; Patch = patch; Message = message; }
private void Error(ISnPatch patch, PatchExecutionErrorType errorType, string message) { _context.Errors.Add(new PatchExecutionError(errorType, patch, message)); }
private void Log(ISnPatch patch, PatchExecutionEventType eventType) { _context.LogCallback?.Invoke(new PatchExecutionLogRecord(eventType, patch)); }
private void WriteInitialStateToDb(ISnPatch patch) { PackageManager.SaveInitialPackage(Manifest.Create(patch)); }