diff --git a/src/ByteSync.Client/Assets/Resources/Resources.Designer.cs b/src/ByteSync.Client/Assets/Resources/Resources.Designer.cs
index 31d07002..3016310f 100644
--- a/src/ByteSync.Client/Assets/Resources/Resources.Designer.cs
+++ b/src/ByteSync.Client/Assets/Resources/Resources.Designer.cs
@@ -270,27 +270,27 @@ internal static string AtomicActionEdit_Action {
///
/// Recherche une chaîne localisée semblable à Copy content.
///
- internal static string AtomicActionEdit_SynchronizeContent {
+ internal static string AtomicActionEdit_CopyContent {
get {
- return ResourceManager.GetString("AtomicActionEdit_SynchronizeContent", resourceCulture);
+ return ResourceManager.GetString("AtomicActionEdit_CopyContent", resourceCulture);
}
}
///
/// Recherche une chaîne localisée semblable à Copy content and date.
///
- internal static string AtomicActionEdit_SynchronizeContentAndDate {
+ internal static string AtomicActionEdit_Copy {
get {
- return ResourceManager.GetString("AtomicActionEdit_SynchronizeContentAndDate", resourceCulture);
+ return ResourceManager.GetString("AtomicActionEdit_Copy", resourceCulture);
}
}
///
/// Recherche une chaîne localisée semblable à Copy date.
///
- internal static string AtomicActionEdit_SynchronizeDate {
+ internal static string AtomicActionEdit_CopyDate {
get {
- return ResourceManager.GetString("AtomicActionEdit_SynchronizeDate", resourceCulture);
+ return ResourceManager.GetString("AtomicActionEdit_CopyDate", resourceCulture);
}
}
@@ -1596,27 +1596,27 @@ internal static string Status_Error {
///
/// Recherche une chaîne localisée semblable à Copy content.
///
- internal static string SynchronizationActionDescription_Action_SynchronizeContent {
+ internal static string SynchronizationActionDescription_Action_CopyContent {
get {
- return ResourceManager.GetString("SynchronizationActionDescription_Action_SynchronizeContent", resourceCulture);
+ return ResourceManager.GetString("SynchronizationActionDescription_Action_CopyContent", resourceCulture);
}
}
///
/// Recherche une chaîne localisée semblable à Copy content and date.
///
- internal static string SynchronizationActionDescription_Action_SynchronizeContentAndDate {
+ internal static string SynchronizationActionDescription_Action_Copy {
get {
- return ResourceManager.GetString("SynchronizationActionDescription_Action_SynchronizeContentAndDate", resourceCulture);
+ return ResourceManager.GetString("SynchronizationActionDescription_Action_Copy", resourceCulture);
}
}
///
/// Recherche une chaîne localisée semblable à Copy date.
///
- internal static string SynchronizationActionDescription_Action_SynchronizeDate {
+ internal static string SynchronizationActionDescription_Action_CopyDate {
get {
- return ResourceManager.GetString("SynchronizationActionDescription_Action_SynchronizeDate", resourceCulture);
+ return ResourceManager.GetString("SynchronizationActionDescription_Action_CopyDate", resourceCulture);
}
}
@@ -2910,14 +2910,23 @@ internal static string ValidationFailure_DuplicateActionNotAllowed {
get {
return ResourceManager.GetString("ValidationFailure_DuplicateActionNotAllowed", resourceCulture);
}
+ }
+
+ ///
+ /// Looks up a localized string similar to Multiple sources found.
+ ///
+ internal static string ValidationFailure_SourceHasMultipleIdentities {
+ get {
+ return ResourceManager.GetString("ValidationFailure_SourceHasMultipleIdentities", resourceCulture);
+ }
}
///
- /// Looks up a localized string similar to Invalid source data.
+ /// Looks up a localized string similar to Source not found.
///
- internal static string ValidationFailure_InvalidSourceCount {
+ internal static string ValidationFailure_SourceMissing {
get {
- return ResourceManager.GetString("ValidationFailure_InvalidSourceCount", resourceCulture);
+ return ResourceManager.GetString("ValidationFailure_SourceMissing", resourceCulture);
}
}
@@ -3201,3 +3210,6 @@ internal static string SynchronizationConfirmation_Cancel {
}
}
}
+
+
+
diff --git a/src/ByteSync.Client/Assets/Resources/Resources.fr.resx b/src/ByteSync.Client/Assets/Resources/Resources.fr.resx
index 95a924af..05aff7fd 100644
--- a/src/ByteSync.Client/Assets/Resources/Resources.fr.resx
+++ b/src/ByteSync.Client/Assets/Resources/Resources.fr.resx
@@ -235,13 +235,13 @@
Destination
-
+
Copier
-
+
Copier le contenu uniquement
-
+
Copier les dates uniquement
@@ -289,13 +289,13 @@
Effectuer l'action suivante :
-
+
Copier le contenu uniquement
-
+
Copier
-
+
Copier les dates uniquement
@@ -1654,8 +1654,11 @@ Voulez-vous enregistrer ce nouveau Profil de Session avec ce nom ?
Destination requise pour la création
-
- Données source invalides
+
+ Source introuvable
+
+
+ Plusieurs sources trouvées
Erreur d'analyse de la source
diff --git a/src/ByteSync.Client/Assets/Resources/Resources.resx b/src/ByteSync.Client/Assets/Resources/Resources.resx
index 7b239a17..27402526 100644
--- a/src/ByteSync.Client/Assets/Resources/Resources.resx
+++ b/src/ByteSync.Client/Assets/Resources/Resources.resx
@@ -131,7 +131,7 @@
of the following conditions are met:
- Contents
+ Content
Last write time
@@ -232,13 +232,13 @@
Destination
-
+
Copy
-
- Copy contents only
+
+ Copy content only
-
+
Copy dates only
@@ -289,13 +289,13 @@
Do the following action:
-
- Copy contents only
+
+ Copy content only
-
+
Copy dates only
-
+
Copy
@@ -353,7 +353,7 @@ Would you like to continue ?
If
- Contents
+ Content
Date
@@ -1696,8 +1696,11 @@ Do you want to save this new Session Profile with this name?
Destination required for create
-
- Invalid source data
+
+ Source not found
+
+
+ Multiple sources found
Source has analysis error
diff --git a/src/ByteSync.Client/Business/Actions/Shared/SharedActionsGroup.cs b/src/ByteSync.Client/Business/Actions/Shared/SharedActionsGroup.cs
index ba75d516..5851e510 100644
--- a/src/ByteSync.Client/Business/Actions/Shared/SharedActionsGroup.cs
+++ b/src/ByteSync.Client/Business/Actions/Shared/SharedActionsGroup.cs
@@ -11,43 +11,34 @@ public SharedActionsGroup()
{
Targets = new HashSet();
}
-
+
public PathIdentity PathIdentity { get; set; } = null!;
-
+
public SharedDataPart? Source { get; set; }
-
+
public HashSet Targets { get; set; }
-
+
public SynchronizationTypes? SynchronizationType { get; set; }
-
+
public SynchronizationStatus? SynchronizationStatus { get; set; }
-
+
public bool IsFromSynchronizationRule { get; set; }
-
+
public string LinkingKeyValue
{
- get
- {
- return PathIdentity.LinkingKeyValue;
- }
+ get { return PathIdentity.LinkingKeyValue; }
}
-
+
public bool IsFile
{
- get
- {
- return PathIdentity.FileSystemType == FileSystemTypes.File;
- }
+ get { return PathIdentity.FileSystemType == FileSystemTypes.File; }
}
-
+
public bool IsDirectory
{
- get
- {
- return PathIdentity.FileSystemType == FileSystemTypes.Directory;
- }
+ get { return PathIdentity.FileSystemType == FileSystemTypes.Directory; }
}
-
+
public string Key
{
get
@@ -62,27 +53,27 @@ public string Key
+ SynchronizationType;
}
}
-
+
public string GetSourceFullName()
{
string sourceFileName = GetFullName(Source!);
-
+
return sourceFileName;
}
-
+
public HashSet GetTargetsFullNames(ByteSyncEndpoint endpoint)
{
HashSet result = new HashSet();
-
+
foreach (var target in Targets.Where(sdp => Equals(sdp.ClientInstanceId, endpoint.ClientInstanceId)))
{
var fullName = GetFullName(target);
result.Add(fullName);
}
-
+
return result;
}
-
+
public string GetFullName(SharedDataPart sharedDataPart)
{
string sourceFileName;
@@ -94,36 +85,37 @@ public string GetFullName(SharedDataPart sharedDataPart)
{
sourceFileName = sharedDataPart.RootPath;
}
-
+
return sourceFileName;
}
-
+
private bool Equals(SharedActionsGroup other)
{
return ActionsGroupId == other.ActionsGroupId;
}
-
+
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
- return Equals((SharedActionsGroup) obj);
+
+ return Equals((SharedActionsGroup)obj);
}
-
+
public override int GetHashCode()
{
return ActionsGroupId.GetHashCode();
}
-
+
public ActionsGroupDefinition GetDefinition()
{
var actionsGroupDefinition = new ActionsGroupDefinition();
-
+
actionsGroupDefinition.ActionsGroupId = ActionsGroupId;
actionsGroupDefinition.FileSystemType = PathIdentity.FileSystemType;
actionsGroupDefinition.Operator = Operator;
- actionsGroupDefinition.AppliesOnlySynchronizeDate = AppliesOnlySynchronizeDate;
+ actionsGroupDefinition.AppliesOnlyCopyDate = AppliesOnlyCopyDate;
actionsGroupDefinition.SourceClientInstanceId = Source?.ClientInstanceId;
actionsGroupDefinition.TargetClientInstanceAndNodeIds = Targets
.Select(t => new ClientInstanceIdAndNodeId
@@ -135,7 +127,7 @@ public ActionsGroupDefinition GetDefinition()
actionsGroupDefinition.Size = Size;
actionsGroupDefinition.CreationTimeUtc = CreationTimeUtc;
actionsGroupDefinition.LastWriteTimeUtc = LastWriteTimeUtc;
-
+
return actionsGroupDefinition;
}
}
\ No newline at end of file
diff --git a/src/ByteSync.Client/Business/Comparisons/AtomicActionValidationFailureReason.cs b/src/ByteSync.Client/Business/Comparisons/AtomicActionValidationFailureReason.cs
index dbaa4f83..87d0d760 100644
--- a/src/ByteSync.Client/Business/Comparisons/AtomicActionValidationFailureReason.cs
+++ b/src/ByteSync.Client/Business/Comparisons/AtomicActionValidationFailureReason.cs
@@ -17,9 +17,10 @@ public enum AtomicActionValidationFailureReason
DestinationRequiredForCreateOperation = 22,
// Advanced Consistency - Source Issues
- InvalidSourceCount = 30,
SourceHasAnalysisError = 31,
SourceNotAccessible = 32,
+ SourceMissing = 33,
+ SourceHasMultipleIdentities = 34,
// Advanced Consistency - Target Issues
TargetFileNotPresent = 40,
diff --git a/src/ByteSync.Client/Business/Filtering/Evaluators/ActionComparisonExpressionEvaluator.cs b/src/ByteSync.Client/Business/Filtering/Evaluators/ActionComparisonExpressionEvaluator.cs
index f01d20c4..e4535297 100644
--- a/src/ByteSync.Client/Business/Filtering/Evaluators/ActionComparisonExpressionEvaluator.cs
+++ b/src/ByteSync.Client/Business/Filtering/Evaluators/ActionComparisonExpressionEvaluator.cs
@@ -1,16 +1,16 @@
-using ByteSync.Business.Actions.Local;
+using System.Text.RegularExpressions;
+using ByteSync.Business.Actions.Local;
using ByteSync.Business.Filtering.Expressions;
using ByteSync.Business.Filtering.Parsing;
-using ByteSync.Models.Comparisons.Result;
-using System.Text.RegularExpressions;
using ByteSync.Interfaces.Repositories;
+using ByteSync.Models.Comparisons.Result;
namespace ByteSync.Business.Filtering.Evaluators;
public class ActionComparisonExpressionEvaluator : ExpressionEvaluator
{
private readonly IAtomicActionRepository _actionRepository;
-
+
public ActionComparisonExpressionEvaluator(IAtomicActionRepository actionRepository)
{
_actionRepository = actionRepository;
@@ -35,7 +35,7 @@ public override bool Evaluate(ActionComparisonExpression expression, ComparisonI
_ => throw new ArgumentException($"Unsupported operator for actions: {expression.Operator}")
};
}
-
+
private int GetActionCount(ComparisonItem item, string[] pathParts)
{
// The base of the path is always "actions"
@@ -43,10 +43,10 @@ private int GetActionCount(ComparisonItem item, string[] pathParts)
{
return 0;
}
-
+
// Get the full list of actions
var allActions = _actionRepository.GetAtomicActions(item);
-
+
if (!allActions.Any())
{
return 0;
@@ -82,7 +82,7 @@ private int GetActionCount(ComparisonItem item, string[] pathParts)
private int FilterActionsByType(List actions, string actionType)
{
var actionTypePattern = new Regex(
- @"^([a-zA-Z-]+)$",
+ @"^([a-zA-Z-]+)$",
RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(500));
var match = actionTypePattern.Match(actionType);
@@ -94,13 +94,13 @@ private int FilterActionsByType(List actions, string actionType)
return normalizedActionType switch
{
- Identifiers.ACTION_COPY_CONTENTS => actions.Count(a => a.IsSynchronizeContent),
- Identifiers.ACTION_COPY => actions.Count(a => a.IsSynchronizeContentAndDate),
- Identifiers.ACTION_COPY_DATES => actions.Count(a => a.IsSynchronizeDate),
+ Identifiers.ACTION_COPY_CONTENT => actions.Count(a => a.IsCopyContent),
+ Identifiers.ACTION_COPY => actions.Count(a => a.IsFullCopy),
+ Identifiers.ACTION_COPY_DATES => actions.Count(a => a.IsCopyDates),
Identifiers.ACTION_SYNCHRONIZE_DELETE => actions.Count(a => a.IsDelete),
Identifiers.ACTION_SYNCHRONIZE_CREATE => actions.Count(a => a.IsCreate),
Identifiers.ACTION_DO_NOTHING => actions.Count(a => a.IsDoNothing),
_ => 0
};
}
-}
+}
\ No newline at end of file
diff --git a/src/ByteSync.Client/Business/Filtering/Evaluators/PropertyValueExtractor.cs b/src/ByteSync.Client/Business/Filtering/Evaluators/PropertyValueExtractor.cs
index 0247d068..c9a1040d 100644
--- a/src/ByteSync.Client/Business/Filtering/Evaluators/PropertyValueExtractor.cs
+++ b/src/ByteSync.Client/Business/Filtering/Evaluators/PropertyValueExtractor.cs
@@ -1,4 +1,5 @@
-using ByteSync.Business.Comparisons;
+using System.IO;
+using ByteSync.Business.Comparisons;
using ByteSync.Business.Filtering.Parsing;
using ByteSync.Business.Filtering.Values;
using ByteSync.Interfaces.Services.Filtering;
@@ -15,50 +16,50 @@ public PropertyValueCollection GetPropertyValue(ComparisonItem item, DataPart? d
{
return GetGeneralPropertyValue(item, property);
}
-
+
// Find content identities for the specific data source
var contentIdentities = item.GetContentIdentities(dataPart.GetApplicableInventoryPart());
-
+
var propertyLower = property.ToLowerInvariant();
var propertyActions = new Dictionary>
{
- { Identifiers.PROPERTY_CONTENTS, () => ExtractContent(contentIdentities) },
- { Identifiers.PROPERTY_CONTENTS_AND_DATE, () => ExtractContentAndDate(contentIdentities, dataPart) },
+ { Identifiers.PROPERTY_CONTENT, () => ExtractContent(contentIdentities) },
+ { Identifiers.PROPERTY_CONTENT_AND_DATE, () => ExtractContentAndDate(contentIdentities, dataPart) },
{ Identifiers.PROPERTY_SIZE, () => ExtractSize(contentIdentities, dataPart) },
{ Identifiers.PROPERTY_LAST_WRITE_TIME, () => ExtractLastWriteTime(contentIdentities, dataPart) },
};
-
+
if (propertyActions.TryGetValue(propertyLower, out var action))
{
return action();
}
-
+
throw new ArgumentException($"Unknown property: {property}");
}
-
+
private PropertyValueCollection ExtractContent(List contentIdentities)
{
var contents = new HashSet();
-
+
foreach (var contentIdentity in contentIdentities)
{
contents.Add(contentIdentity.Core!.SignatureHash!);
}
-
+
var result = new PropertyValueCollection();
foreach (var content in contents)
{
result.Add(new PropertyValue(content));
}
-
+
return result;
}
private PropertyValueCollection ExtractContentAndDate(List contentIdentities, DataPart dataPart)
{
var contents = new HashSet();
-
+
foreach (var contentIdentity in contentIdentities)
{
var signatureHash = contentIdentity.Core!.SignatureHash;
@@ -66,20 +67,20 @@ private PropertyValueCollection ExtractContentAndDate(List cont
contents.Add($"{signatureHash}_{lastWriteTime?.Ticks}");
}
-
+
var result = new PropertyValueCollection();
foreach (var content in contents)
{
result.Add(new PropertyValue(content));
}
-
+
return result;
}
private PropertyValueCollection ExtractSize(List contentIdentities, DataPart dataPart)
{
var contents = new HashSet();
-
+
foreach (var contentIdentity in contentIdentities)
{
foreach (var fileSystemDescription in contentIdentity.GetFileSystemDescriptions(dataPart.GetApplicableInventoryPart()))
@@ -90,20 +91,20 @@ private PropertyValueCollection ExtractSize(List contentIdentit
}
}
}
-
+
var result = new PropertyValueCollection();
foreach (var content in contents)
{
result.Add(new PropertyValue(content));
}
-
+
return result;
}
private PropertyValueCollection ExtractLastWriteTime(List contentIdentities, DataPart dataPart)
{
var contents = new HashSet();
-
+
foreach (var contentIdentity in contentIdentities)
{
foreach (var fileSystemDescription in contentIdentity.GetFileSystemDescriptions(dataPart.GetApplicableInventoryPart()))
@@ -114,39 +115,42 @@ private PropertyValueCollection ExtractLastWriteTime(List conte
}
}
}
-
+
var result = new PropertyValueCollection();
foreach (var content in contents)
{
result.Add(new PropertyValue(content));
}
-
+
return result;
}
-
+
///
/// Gets property value that's not specific to a data source
///
private PropertyValueCollection GetGeneralPropertyValue(ComparisonItem item, string property)
{
var propertyLower = property.ToLowerInvariant();
-
+
object value;
switch (propertyLower)
{
case "ext":
- value = System.IO.Path.GetExtension(item.PathIdentity.FileName).TrimStart('.');
+ value = Path.GetExtension(item.PathIdentity.FileName).TrimStart('.');
+
break;
case "name":
- value = System.IO.Path.GetFileNameWithoutExtension(item.PathIdentity.FileName);
+ value = Path.GetFileNameWithoutExtension(item.PathIdentity.FileName);
+
break;
case "path":
value = item.PathIdentity.FileName;
+
break;
default:
throw new ArgumentException($"Property '{property}' requires a data source");
}
-
+
var result = new PropertyValueCollection();
result.Add(new PropertyValue(value));
diff --git a/src/ByteSync.Client/Business/Filtering/Parsing/Identifiers.cs b/src/ByteSync.Client/Business/Filtering/Parsing/Identifiers.cs
index dfbd5de4..1e56087f 100644
--- a/src/ByteSync.Client/Business/Filtering/Parsing/Identifiers.cs
+++ b/src/ByteSync.Client/Business/Filtering/Parsing/Identifiers.cs
@@ -1,9 +1,11 @@
-namespace ByteSync.Business.Filtering.Parsing;
+using System.Reflection;
+
+namespace ByteSync.Business.Filtering.Parsing;
public class Identifiers
{
public const string ACTION_COPY = "copy";
- public const string ACTION_COPY_CONTENTS = "copy-contents";
+ public const string ACTION_COPY_CONTENT = "copy-content";
public const string ACTION_COPY_DATES = "copy-dates";
public const string ACTION_SYNCHRONIZE_CREATE = "create";
public const string ACTION_SYNCHRONIZE_DELETE = "delete";
@@ -18,8 +20,8 @@ public class Identifiers
public const string OPERATOR_NAME = "name";
public const string OPERATOR_PATH = "path";
- public const string PROPERTY_CONTENTS = "contents";
- public const string PROPERTY_CONTENTS_AND_DATE = "contents-and-date";
+ public const string PROPERTY_CONTENT = "content";
+ public const string PROPERTY_CONTENT_AND_DATE = "content-and-date";
public const string PROPERTY_LAST_WRITE_TIME = "last-write-time";
public const string PROPERTY_SIZE = "size";
@@ -28,15 +30,15 @@ public class Identifiers
public const string PROPERTY_DIR = "dir";
public const string PROPERTY_PLACEHOLDER = "_";
-
+
private static List? _cachedAll;
-
+
public static List All()
{
if (_cachedAll == null)
{
_cachedAll = typeof(Identifiers)
- .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy)
+ .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(field => field.IsLiteral && !field.IsInitOnly && field.FieldType == typeof(string))
.Select(field => (string)field.GetValue(null))
.ToList();
@@ -44,4 +46,4 @@ public static List All()
return _cachedAll;
}
-}
+}
\ No newline at end of file
diff --git a/src/ByteSync.Client/Services/Actions/SharedActionsGroupComputer.cs b/src/ByteSync.Client/Services/Actions/SharedActionsGroupComputer.cs
index 9c1af3c2..b2bc6409 100644
--- a/src/ByteSync.Client/Services/Actions/SharedActionsGroupComputer.cs
+++ b/src/ByteSync.Client/Services/Actions/SharedActionsGroupComputer.cs
@@ -1,9 +1,6 @@
using System.Threading;
-using System.Threading.Tasks;
using ByteSync.Business.Actions.Shared;
-using ByteSync.Business.Inventories;
using ByteSync.Common.Business.Actions;
-using ByteSync.Common.Helpers;
using ByteSync.Interfaces.Controls.Actions;
using ByteSync.Interfaces.Repositories;
@@ -13,37 +10,40 @@ public class SharedActionsGroupComputer : ISharedActionsGroupComputer
{
private readonly ISharedAtomicActionRepository _sharedAtomicActionRepository;
private readonly ISharedActionsGroupRepository _sharedActionsGroupRepository;
-
+
private List _buffer;
- private int _counter;
+ private int _counter;
private readonly object _lock = new();
-
- public SharedActionsGroupComputer(ISharedAtomicActionRepository sharedAtomicActionRepository, ISharedActionsGroupRepository sharedActionsGroupRepository)
+
+ public SharedActionsGroupComputer(ISharedAtomicActionRepository sharedAtomicActionRepository,
+ ISharedActionsGroupRepository sharedActionsGroupRepository)
{
_sharedAtomicActionRepository = sharedAtomicActionRepository;
_sharedActionsGroupRepository = sharedActionsGroupRepository;
_buffer = new List();
-
+
_counter = 0;
}
-
+
public async Task ComputeSharedActionsGroups()
{
var sharedAtomicActions = _sharedAtomicActionRepository.Elements;
-
+
var dictionary = sharedAtomicActions.GroupBy(saa => saa.PathIdentity)
.ToDictionary(g => g.Key, g => g.ToList());
- await Parallel.ForEachAsync(dictionary, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }, (pair, _) =>
- {
- ComputeGroups_CopyContentAndDate(pair.Value);
- ComputeGroups_CopyContent(pair.Value);
- ComputeGroups_CopyDate(pair.Value);
- ComputeGroups_Create(pair.Value);
- ComputeGroups_Delete(pair.Value);
- return ValueTask.CompletedTask;
- });
-
+ await Parallel.ForEachAsync(dictionary, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 },
+ (pair, _) =>
+ {
+ ComputeGroups_CopyContentAndDate(pair.Value);
+ ComputeGroups_CopyContent(pair.Value);
+ ComputeGroups_CopyDate(pair.Value);
+ ComputeGroups_Create(pair.Value);
+ ComputeGroups_Delete(pair.Value);
+
+ return ValueTask.CompletedTask;
+ });
+
lock (_lock)
{
if (_buffer.Count > 0)
@@ -53,18 +53,18 @@ public async Task ComputeSharedActionsGroups()
}
}
}
-
+
private void ComputeGroups_CopyContentAndDate(List atomicActions)
{
- var sharedAtomicActions = GetSharedAtomicActions(atomicActions, ActionOperatorTypes.SynchronizeContentAndDate);
+ var sharedAtomicActions = GetSharedAtomicActions(atomicActions, ActionOperatorTypes.Copy);
var groups = GetCopyGroups(sharedAtomicActions, true);
-
+
var sharedActionsGroups = new List();
foreach (var group in groups)
{
- var sharedActionsGroup = BuildSharedActionsGroup(ActionOperatorTypes.SynchronizeContentAndDate);
-
+ var sharedActionsGroup = BuildSharedActionsGroup(ActionOperatorTypes.Copy);
+
sharedActionsGroup.Source = group.First().Source;
sharedActionsGroup.Targets.AddAll(group.Select(saa => saa.Target!));
sharedActionsGroup.PathIdentity = group.First().PathIdentity;
@@ -73,30 +73,30 @@ private void ComputeGroups_CopyContentAndDate(List atomicAct
sharedActionsGroup.Size = group.First().Size;
sharedActionsGroup.SynchronizationType = group.First().SynchronizationType;
sharedActionsGroup.IsFromSynchronizationRule = group.First().IsFromSynchronizationRule;
-
- if (sharedActionsGroup.Targets.All(t => t.SignatureHash != null
+
+ if (sharedActionsGroup.Targets.All(t => t.SignatureHash != null
&& t.SignatureHash!.Equals(sharedActionsGroup.Source!.SignatureHash)))
{
- sharedActionsGroup.AppliesOnlySynchronizeDate = true;
+ sharedActionsGroup.AppliesOnlyCopyDate = true;
}
-
+
sharedActionsGroups.Add(sharedActionsGroup);
}
AddSharedActionsGroups(sharedActionsGroups);
}
-
+
private void ComputeGroups_CopyContent(List atomicActions)
{
- var sharedAtomicActions = GetSharedAtomicActions(atomicActions, ActionOperatorTypes.SynchronizeContentOnly);
-
+ var sharedAtomicActions = GetSharedAtomicActions(atomicActions, ActionOperatorTypes.CopyContentOnly);
+
var groups = GetCopyGroups(sharedAtomicActions, false);
-
+
var sharedActionsGroups = new List();
foreach (var group in groups)
{
- var sharedActionsGroup = BuildSharedActionsGroup(ActionOperatorTypes.SynchronizeContentOnly);
-
+ var sharedActionsGroup = BuildSharedActionsGroup(ActionOperatorTypes.CopyContentOnly);
+
sharedActionsGroup.Source = group.First().Source;
sharedActionsGroup.Targets.AddAll(group.Select(saa => saa.Target!));
sharedActionsGroup.PathIdentity = group.First().PathIdentity;
@@ -111,19 +111,19 @@ private void ComputeGroups_CopyContent(List atomicActions)
AddSharedActionsGroups(sharedActionsGroups);
}
-
+
private void ComputeGroups_CopyDate(List atomicActions)
{
- var sharedAtomicActions = GetSharedAtomicActions(atomicActions, ActionOperatorTypes.SynchronizeDate);
-
+ var sharedAtomicActions = GetSharedAtomicActions(atomicActions, ActionOperatorTypes.CopyDatesOnly);
+
var sharedActionsGroups = new List();
- foreach (KeyValuePair> pair in
+ foreach (KeyValuePair> pair in
sharedAtomicActions.GroupBy(aa => aa.Source!)
.ToDictionary(g => g.Key, g => g.ToList()))
{
- var sharedActionsGroup = BuildSharedActionsGroup(ActionOperatorTypes.SynchronizeDate);
-
+ var sharedActionsGroup = BuildSharedActionsGroup(ActionOperatorTypes.CopyDatesOnly);
+
sharedActionsGroup.Source = pair.Key;
sharedActionsGroup.Targets.AddAll(pair.Value.Select(saa => saa.Target!));
sharedActionsGroup.PathIdentity = pair.Value.First().PathIdentity;
@@ -132,7 +132,7 @@ private void ComputeGroups_CopyDate(List atomicActions)
sharedActionsGroup.IsFromSynchronizationRule = pair.Value.First().IsFromSynchronizationRule;
AffectSharedActionsGroupId(sharedActionsGroup, pair.Value);
-
+
sharedActionsGroups.Add(sharedActionsGroup);
}
@@ -148,23 +148,23 @@ private void ComputeGroups_Delete(List atomicActions)
{
DoComputeGroups_CreateDelete(ActionOperatorTypes.Delete, atomicActions);
}
-
+
private void DoComputeGroups_CreateDelete(ActionOperatorTypes operatorType, List atomicActions)
{
if (!operatorType.In(ActionOperatorTypes.Create, ActionOperatorTypes.Delete))
{
throw new ArgumentOutOfRangeException(nameof(operatorType));
}
-
+
var sharedAtomicActions = GetSharedAtomicActions(atomicActions, operatorType).ToList();
-
+
if (sharedAtomicActions.Count == 0)
{
return;
}
-
+
var sharedActionsGroup = BuildSharedActionsGroup(operatorType);
-
+
sharedActionsGroup.Source = null;
sharedActionsGroup.Targets.AddAll(sharedAtomicActions.Select(saa => saa.Target!));
sharedActionsGroup.PathIdentity = sharedAtomicActions.First().PathIdentity;
@@ -174,15 +174,16 @@ private void DoComputeGroups_CreateDelete(ActionOperatorTypes operatorType, List
AddSharedActionsGroup(sharedActionsGroup);
}
-
- private IEnumerable GetSharedAtomicActions(List sharedAtomicActions, ActionOperatorTypes operatorType)
+
+ private IEnumerable GetSharedAtomicActions(List sharedAtomicActions,
+ ActionOperatorTypes operatorType)
{
var automaticActions = sharedAtomicActions
.Where(vm => vm.Operator == operatorType);
return automaticActions;
}
-
+
private SharedActionsGroup BuildSharedActionsGroup(ActionOperatorTypes operatorType)
{
var group = new SharedActionsGroup();
@@ -196,6 +197,7 @@ private SharedActionsGroup BuildSharedActionsGroup(ActionOperatorTypes operatorT
private string GenerateUniqueId()
{
var newCounter = Interlocked.Increment(ref _counter);
+
return $"AGID_{newCounter}";
}
@@ -212,12 +214,11 @@ private void AddSharedActionsGroups(List sharedActionsGroups
lock (_lock)
{
_buffer.AddAll(sharedActionsGroups);
-
+
CheckBuffer();
}
-
}
-
+
private void AddSharedActionsGroup(SharedActionsGroup sharedActionsGroup)
{
lock (_lock)
@@ -227,7 +228,7 @@ private void AddSharedActionsGroup(SharedActionsGroup sharedActionsGroup)
CheckBuffer();
}
}
-
+
private void CheckBuffer()
{
if (_buffer.Count > 500)
@@ -236,7 +237,7 @@ private void CheckBuffer()
_buffer.Clear();
}
}
-
+
private static List> GetCopyGroups(IEnumerable sharedAtomicActions, bool isContentAndDate)
{
var root = sharedAtomicActions
@@ -261,7 +262,7 @@ private static List> GetCopyGroups(IEnumerable> lists = new List>();
foreach (var perSource in root)
@@ -284,7 +285,7 @@ private static List> GetCopyGroups(IEnumerable> ComputeSharedAtomicActions()
{
var atomicActions = _atomicActionRepository.Elements;
-
+
foreach (var atomicAction in atomicActions.Where(a => !a.IsDoNothing))
{
CreateSharedAtomicActions(atomicAction, atomicAction.Destination, atomicAction.Source);
}
-
+
return Task.FromResult(_sharedAtomicActionRepository.Elements.ToList());
}
-
+
private void CreateSharedAtomicActions(AtomicAction atomicAction, DataPart? target, DataPart? source)
{
SharedDataPart? sourceSharedDataPart = null;
HashSet? targetSharedDataParts = null;
-
+
SynchronizationTypes? synchronizationType = null;
long? size = null;
DateTime? lastWriteTimeUtc = null;
DateTime? creationTimeUtc = null;
-
+
if (atomicAction.IsDelete)
{
var targetContentIdentities = atomicAction.ComparisonItem!.GetContentIdentities(target!.GetApplicableInventoryPart());
-
- targetSharedDataParts = new HashSet();
+
+ targetSharedDataParts = [];
foreach (var contentIdentity in targetContentIdentities)
{
targetSharedDataParts.AddAll(BuildSharedDataPart(target, contentIdentity));
}
}
-
+
if (atomicAction.IsCreate)
{
- SharedDataPart targetSharedDataPart = DoBuildSharedDataPart(target!, atomicAction.PathIdentity!.LinkingKeyValue);
-
- targetSharedDataParts = new HashSet { targetSharedDataPart };
+ var targetSharedDataPart = DoBuildSharedDataPart(target!, atomicAction.PathIdentity!.LinkingKeyValue);
+
+ targetSharedDataParts = [targetSharedDataPart];
}
- else if (atomicAction.IsSynchronizeContent || atomicAction.IsSynchronizeDate)
+ else if (atomicAction.IsCopyContent || atomicAction.IsCopyDates)
{
var sourceContentIdentities = atomicAction.ComparisonItem!.GetContentIdentities(source!.GetApplicableInventoryPart());
var targetContentIdentities = atomicAction.ComparisonItem!.GetContentIdentities(target!.GetApplicableInventoryPart());
-
+
if (sourceContentIdentities.Count != 1)
{
throw new ApplicationException("sourceContentIdentityViews.Count != 1 -- " + sourceContentIdentities.Count);
}
-
+
var sourceContentIdentity = sourceContentIdentities.Single();
-
+
size = sourceContentIdentity.Core?.Size;
lastWriteTimeUtc = sourceContentIdentity.GetLastWriteTimeUtc(source.GetApplicableInventoryPart());
creationTimeUtc = sourceContentIdentity.GetCreationTimeUtc(source.GetApplicableInventoryPart());
-
+
if (targetContentIdentities.Count > 0)
{
synchronizationType = SynchronizationTypes.Delta;
-
+
sourceSharedDataPart = BuildSharedDataPart(source, sourceContentIdentity).First();
-
- targetSharedDataParts = new HashSet();
+
+ targetSharedDataParts = [];
foreach (var targetContentIdentity in targetContentIdentities)
{
if (!Equals(targetContentIdentity.Core?.SignatureHash,
@@ -100,93 +98,93 @@ private void CreateSharedAtomicActions(AtomicAction atomicAction, DataPart? targ
else
{
synchronizationType = SynchronizationTypes.Full;
-
+
sourceSharedDataPart = BuildSharedDataPart(source, sourceContentIdentity).First();
-
- SharedDataPart targetSharedDataPart = DoBuildSharedDataPart(target,
+
+ var targetSharedDataPart = DoBuildSharedDataPart(target,
sourceSharedDataPart.RelativePath);
-
- targetSharedDataParts = new HashSet { targetSharedDataPart };
+
+ targetSharedDataParts = [targetSharedDataPart];
}
}
-
+
if (targetSharedDataParts == null || targetSharedDataParts.Count == 0)
{
- CreateSharedAtomicAction(atomicAction.AtomicActionId, sourceSharedDataPart, null,
- atomicAction.Operator, atomicAction.PathIdentity!, synchronizationType,
+ CreateSharedAtomicAction(atomicAction.AtomicActionId, sourceSharedDataPart, null,
+ atomicAction.Operator, atomicAction.PathIdentity!, synchronizationType,
size, lastWriteTimeUtc, creationTimeUtc, atomicAction.IsFromSynchronizationRule);
}
else
{
- foreach (SharedDataPart targetSharedDataPart in targetSharedDataParts)
+ foreach (var targetSharedDataPart in targetSharedDataParts)
{
- CreateSharedAtomicAction(atomicAction.AtomicActionId, sourceSharedDataPart, targetSharedDataPart,
- atomicAction.Operator, atomicAction.PathIdentity!, synchronizationType,
+ CreateSharedAtomicAction(atomicAction.AtomicActionId, sourceSharedDataPart, targetSharedDataPart,
+ atomicAction.Operator, atomicAction.PathIdentity!, synchronizationType,
size, lastWriteTimeUtc, creationTimeUtc, atomicAction.IsFromSynchronizationRule);
}
}
}
-
- private void CreateSharedAtomicAction(string atomicActionId, SharedDataPart? sourceSharedDataPart,
- SharedDataPart? targetSharedDataPart, ActionOperatorTypes operatorType, PathIdentity pathIdentity,
+
+ private void CreateSharedAtomicAction(string atomicActionId, SharedDataPart? sourceSharedDataPart,
+ SharedDataPart? targetSharedDataPart, ActionOperatorTypes operatorType, PathIdentity pathIdentity,
SynchronizationTypes? synchronizationType, long? size, DateTime? lastWriteTimeUtc, DateTime? creationTimeUtc,
bool isFromSynchronizationRule)
{
var sharedAtomicAction = new SharedAtomicAction(atomicActionId);
-
+
sharedAtomicAction.Operator = operatorType;
sharedAtomicAction.PathIdentity = pathIdentity;
-
+
sharedAtomicAction.Source = sourceSharedDataPart;
sharedAtomicAction.Target = targetSharedDataPart;
-
+
sharedAtomicAction.SynchronizationType = synchronizationType;
sharedAtomicAction.Size = size;
sharedAtomicAction.CreationTimeUtc = creationTimeUtc;
sharedAtomicAction.LastWriteTimeUtc = lastWriteTimeUtc;
-
+
sharedAtomicAction.IsFromSynchronizationRule = isFromSynchronizationRule;
_sharedAtomicActionRepository.AddOrUpdate(sharedAtomicAction);
}
-
+
private HashSet BuildSharedDataPart(DataPart dataPart, ContentIdentity contentIdentity)
{
var result = new HashSet();
-
+
var fileSystemDescriptions = contentIdentity.GetFileSystemDescriptions(dataPart.GetApplicableInventoryPart());
-
+
foreach (var fileSystemDescription in fileSystemDescriptions)
{
// var signatureInfo = BuildSignatureInfo(fileDescription);
-
+
string? signatureGuid = null;
string? signatureHash = null;
- bool hasAnalysisError = false;
-
+ var hasAnalysisError = false;
+
if (fileSystemDescription is FileDescription fileDescription)
{
signatureGuid = fileDescription.SignatureGuid;
signatureHash = contentIdentity.Core?.SignatureHash;
hasAnalysisError = fileDescription.HasAnalysisError;
}
-
- SharedDataPart sharedDataPart = DoBuildSharedDataPart(dataPart,
+
+ var sharedDataPart = DoBuildSharedDataPart(dataPart,
fileSystemDescription.RelativePath, signatureGuid, signatureHash, hasAnalysisError);
-
+
result.Add(sharedDataPart);
}
-
+
return result;
}
-
- private SharedDataPart DoBuildSharedDataPart(DataPart dataPart, string? relativePath = null, string? signatureGuid = null,
+
+ private SharedDataPart DoBuildSharedDataPart(DataPart dataPart, string? relativePath = null, string? signatureGuid = null,
string? signatureHash = null, bool hasAnalysisError = false)
{
var inventory = dataPart.GetAppliableInventory();
var inventoryPart = dataPart.GetApplicableInventoryPart();
-
- SharedDataPart sharedDataPart = new SharedDataPart(
+
+ var sharedDataPart = new SharedDataPart(
dataPart.Name,
inventory,
inventoryPart,
@@ -194,7 +192,7 @@ private SharedDataPart DoBuildSharedDataPart(DataPart dataPart, string? relative
signatureGuid,
signatureHash,
hasAnalysisError);
-
+
return sharedDataPart;
}
}
\ No newline at end of file
diff --git a/src/ByteSync.Client/Services/Communications/Transfers/Downloading/DownloadTargetBuilder.cs b/src/ByteSync.Client/Services/Communications/Transfers/Downloading/DownloadTargetBuilder.cs
index 927699ac..dfa973bf 100644
--- a/src/ByteSync.Client/Services/Communications/Transfers/Downloading/DownloadTargetBuilder.cs
+++ b/src/ByteSync.Client/Services/Communications/Transfers/Downloading/DownloadTargetBuilder.cs
@@ -16,14 +16,13 @@ namespace ByteSync.Services.Communications.Transfers.Downloading;
public class DownloadTargetBuilder : IDownloadTargetBuilder, IDisposable
{
-
private readonly ICloudSessionLocalDataManager _cloudSessionLocalDataManager;
private readonly ISessionProfileLocalDataManager _sessionProfileLocalDataManager;
private readonly ISharedActionsGroupRepository _sharedActionsGroupRepository;
private readonly IConnectionService _connectionService;
private readonly ITemporaryFileManagerFactory _temporaryFileManagerFactory;
private readonly ISessionService _sessionService;
-
+
// Add a cache to ensure singleton DownloadTarget per fileId
private readonly Dictionary _downloadTargetCache = new();
@@ -31,8 +30,9 @@ public class DownloadTargetBuilder : IDownloadTargetBuilder, IDisposable
private readonly IDisposable _sessionSubscription;
private readonly IDisposable _sessionStatusSubscription;
private bool _disposed = false;
-
- public DownloadTargetBuilder(ICloudSessionLocalDataManager cloudSessionLocalDataManager, ISessionProfileLocalDataManager sessionProfileLocalDataManager,
+
+ public DownloadTargetBuilder(ICloudSessionLocalDataManager cloudSessionLocalDataManager,
+ ISessionProfileLocalDataManager sessionProfileLocalDataManager,
ISharedActionsGroupRepository sharedActionsGroupRepository, IConnectionService connectionService,
ITemporaryFileManagerFactory temporaryFileManagerFactory, ISessionService sessionService)
{
@@ -47,7 +47,7 @@ public DownloadTargetBuilder(ICloudSessionLocalDataManager cloudSessionLocalData
_sessionSubscription = _sessionService.SessionObservable
.Where(session => session == null)
.Subscribe(_ => ClearCache());
-
+
_sessionStatusSubscription = _sessionService.SessionStatusObservable
.Where(status => status == SessionStatus.Preparation)
.Subscribe(_ => ClearCache());
@@ -58,7 +58,7 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
// Check cache first
if (_downloadTargetCache.TryGetValue(sharedFileDefinition.Id, out var existing))
return existing;
-
+
LocalSharedFile? sharedFile = null;
var downloadDestinations = new HashSet();
string destinationPath;
@@ -66,7 +66,7 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
Dictionary>? finalDestinationsPerActionsGroupId = null;
Dictionary? datesPerActionsGroupId = null;
List? temporaryFileManagers = null;
-
+
DownloadTarget downloadTarget;
if (sharedFileDefinition.IsInventory)
{
@@ -98,15 +98,15 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
{
var zipDestinationPath = _cloudSessionLocalDataManager.GetSynchronizationTempZipPath(sharedFileDefinition);
downloadDestinations.Add(zipDestinationPath);
-
+
foreach (var actionsGroupId in sharedFileDefinition.ActionsGroupIds!)
{
var sharedActionsGroup = _sharedActionsGroupRepository.GetSharedActionsGroup(actionsGroupId);
var actionsGroupDestinations = sharedActionsGroup!.GetTargetsFullNames(_connectionService.CurrentEndPoint!);
finalDestinationsPerActionsGroupId.Add(actionsGroupId, actionsGroupDestinations);
-
- if (sharedActionsGroup.IsSynchronizeContentAndDate)
+
+ if (sharedActionsGroup.IsFullCopy)
{
datesPerActionsGroupId.Add(actionsGroupId, DownloadTargetDates.FromSharedActionsGroup(sharedActionsGroup));
}
@@ -114,7 +114,8 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
}
else
{
- var sharedActionsGroup = _sharedActionsGroupRepository.GetSharedActionsGroup(sharedFileDefinition.ActionsGroupIds!.Single());
+ var sharedActionsGroup =
+ _sharedActionsGroupRepository.GetSharedActionsGroup(sharedFileDefinition.ActionsGroupIds!.Single());
var finalDestinations = sharedActionsGroup!.GetTargetsFullNames(_connectionService.CurrentEndPoint!);
finalDestinationsPerActionsGroupId.Add(sharedActionsGroup.ActionsGroupId, finalDestinations);
@@ -126,7 +127,7 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
{
var temporaryFileManager = _temporaryFileManagerFactory.Create(finalDestination);
var destinationTemporaryPath = temporaryFileManager.GetDestinationTemporaryPath();
-
+
downloadDestinations.Add(destinationTemporaryPath);
temporaryFileManagers.Add(temporaryFileManager);
}
@@ -139,9 +140,10 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
downloadDestinations.Add(deltaDestination);
}
- if (sharedActionsGroup.IsSynchronizeContentAndDate)
+ if (sharedActionsGroup.IsFullCopy)
{
- datesPerActionsGroupId.Add(sharedActionsGroup.ActionsGroupId, DownloadTargetDates.FromSharedActionsGroup(sharedActionsGroup));
+ datesPerActionsGroupId.Add(sharedActionsGroup.ActionsGroupId,
+ DownloadTargetDates.FromSharedActionsGroup(sharedActionsGroup));
}
}
}
@@ -155,10 +157,10 @@ public DownloadTarget BuildDownloadTarget(SharedFileDefinition sharedFileDefinit
downloadTarget.FinalDestinationsPerActionsGroupId = finalDestinationsPerActionsGroupId;
downloadTarget.LastWriteTimeUtcPerActionsGroupId = datesPerActionsGroupId;
downloadTarget.TemporaryFileManagers = temporaryFileManagers;
-
+
// Store in cache
_downloadTargetCache[sharedFileDefinition.Id] = downloadTarget;
-
+
return downloadTarget;
}
diff --git a/src/ByteSync.Client/Services/Comparisons/AtomicActionConsistencyChecker.cs b/src/ByteSync.Client/Services/Comparisons/AtomicActionConsistencyChecker.cs
index 67a6f491..6e1a850f 100644
--- a/src/ByteSync.Client/Services/Comparisons/AtomicActionConsistencyChecker.cs
+++ b/src/ByteSync.Client/Services/Comparisons/AtomicActionConsistencyChecker.cs
@@ -104,8 +104,8 @@ private AtomicActionValidationResult CanApply(AtomicAction atomicAction, Compari
private static AtomicActionValidationResult CheckBasicConsistency(AtomicAction atomicAction, ComparisonItem comparisonItem)
{
- if (atomicAction.Operator.In(ActionOperatorTypes.SynchronizeContentAndDate, ActionOperatorTypes.SynchronizeContentOnly,
- ActionOperatorTypes.SynchronizeDate))
+ if (atomicAction.Operator.In(ActionOperatorTypes.Copy, ActionOperatorTypes.CopyContentOnly,
+ ActionOperatorTypes.CopyDatesOnly))
{
if (comparisonItem.FileSystemType == FileSystemTypes.Directory)
{
@@ -168,13 +168,13 @@ private AtomicActionValidationResult CheckAdvancedConsistency(AtomicAction atomi
{
var enforceInventoryPartAccessGuard = ShouldEnforceInventoryPartAccessGuard();
- if (atomicAction.Operator.In(ActionOperatorTypes.SynchronizeContentAndDate, ActionOperatorTypes.SynchronizeContentOnly,
- ActionOperatorTypes.SynchronizeDate))
+ if (atomicAction.Operator.In(ActionOperatorTypes.Copy, ActionOperatorTypes.CopyContentOnly,
+ ActionOperatorTypes.CopyDatesOnly))
{
return ValidateSynchronize(atomicAction, comparisonItem, enforceInventoryPartAccessGuard);
}
- if (atomicAction.IsSynchronizeDate || atomicAction.IsDelete)
+ if (atomicAction.IsCopyDates || atomicAction.IsDelete)
{
return ValidateSynchronizeDateOrDelete(atomicAction, comparisonItem, enforceInventoryPartAccessGuard);
}
@@ -229,9 +229,14 @@ private AtomicActionValidationResult ValidateSynchronize(AtomicAction atomicActi
return AtomicActionValidationResult.Failure(AtomicActionValidationFailureReason.SourceNotAccessible);
}
- if (sourceContentIdentities.Count != 1)
+ if (sourceContentIdentities.Count == 0)
{
- return AtomicActionValidationResult.Failure(AtomicActionValidationFailureReason.InvalidSourceCount);
+ return AtomicActionValidationResult.Failure(AtomicActionValidationFailureReason.SourceMissing);
+ }
+
+ if (sourceContentIdentities.Count >= 2)
+ {
+ return AtomicActionValidationResult.Failure(AtomicActionValidationFailureReason.SourceHasMultipleIdentities);
}
var sourceContentIdentity = sourceContentIdentities.Single();
@@ -284,7 +289,7 @@ private AtomicActionValidationResult ValidateSynchronize(AtomicAction atomicActi
return AtomicActionValidationResult.Failure(AtomicActionValidationFailureReason.AtLeastOneTargetsNotAccessible);
}
- if (atomicAction.IsSynchronizeContentOnly && targetContentIdentities.Count != 0 &&
+ if (atomicAction.IsCopyContentOnly && targetContentIdentities.Count != 0 &&
sourceContentIdentity.Core != null &&
targetContentIdentities.All(t => t.Core != null && sourceContentIdentity.Core.Equals(t.Core)))
{
@@ -469,8 +474,8 @@ private AtomicActionValidationResult CheckConsistencyAgainstAlreadySetActions(At
{
var alreadySetAtomicAction = alreadySetAtomicActions.Single();
- if ((!alreadySetAtomicAction.IsSynchronizeDate || !atomicAction.IsSynchronizeContentOnly)
- && (!alreadySetAtomicAction.IsSynchronizeContentOnly || !atomicAction.IsSynchronizeDate))
+ if ((!alreadySetAtomicAction.IsCopyDates || !atomicAction.IsCopyContentOnly)
+ && (!alreadySetAtomicAction.IsCopyContentOnly || !atomicAction.IsCopyDates))
{
return AtomicActionValidationResult.Failure(AtomicActionValidationFailureReason
.DestinationAlreadyUsedByNonComplementaryAction);
diff --git a/src/ByteSync.Client/Services/Comparisons/DescriptionBuilders/AbstractDescriptionBuilder.cs b/src/ByteSync.Client/Services/Comparisons/DescriptionBuilders/AbstractDescriptionBuilder.cs
index f41f2228..539cde08 100644
--- a/src/ByteSync.Client/Services/Comparisons/DescriptionBuilders/AbstractDescriptionBuilder.cs
+++ b/src/ByteSync.Client/Services/Comparisons/DescriptionBuilders/AbstractDescriptionBuilder.cs
@@ -1,7 +1,6 @@
using System.Text;
using ByteSync.Assets.Resources;
using ByteSync.Common.Business.Actions;
-using ByteSync.Interfaces;
using ByteSync.Interfaces.Services.Localizations;
namespace ByteSync.Services.Comparisons.DescriptionBuilders;
@@ -12,19 +11,19 @@ protected AbstractDescriptionBuilder(ILocalizationService localizationService)
{
LocalizationService = localizationService;
}
-
+
protected ILocalizationService LocalizationService { get; }
-
+
public string GetDescription(T element)
{
var stringBuilder = new StringBuilder();
AppendDescription(stringBuilder, element);
var description = stringBuilder.ToString();
-
+
return description;
}
-
+
public abstract void AppendDescription(StringBuilder stringBuilder, T element);
protected string GetAction(AbstractAction action)
@@ -35,34 +34,40 @@ protected string GetAction(AbstractAction action)
protected string GetAction(ActionOperatorTypes operatorType)
{
var result = "";
-
+
switch (operatorType)
{
- case ActionOperatorTypes.SynchronizeContentOnly:
- result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_SynchronizeContent)];
+ case ActionOperatorTypes.CopyContentOnly:
+ result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_CopyContent)];
+
break;
- case ActionOperatorTypes.SynchronizeContentAndDate:
- result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_SynchronizeContentAndDate)];
+ case ActionOperatorTypes.Copy:
+ result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_Copy)];
+
break;
- case ActionOperatorTypes.SynchronizeDate:
- result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_SynchronizeDate)];
+ case ActionOperatorTypes.CopyDatesOnly:
+ result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_CopyDate)];
+
break;
case ActionOperatorTypes.Create:
result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_Create)];
+
break;
case ActionOperatorTypes.Delete:
result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_Delete)];
+
break;
case ActionOperatorTypes.DoNothing:
result = LocalizationService[nameof(Resources.SynchronizationActionDescription_Action_DoNothing)];
+
break;
}
-
+
if (result.IsEmpty())
{
throw new ApplicationException("Unknown actionsGroup.Operator " + operatorType);
}
-
+
return result;
}
diff --git a/src/ByteSync.Client/Services/Synchronizations/SynchronizationActionHandler.cs b/src/ByteSync.Client/Services/Synchronizations/SynchronizationActionHandler.cs
index 155e84eb..ec538544 100644
--- a/src/ByteSync.Client/Services/Synchronizations/SynchronizationActionHandler.cs
+++ b/src/ByteSync.Client/Services/Synchronizations/SynchronizationActionHandler.cs
@@ -25,7 +25,7 @@ public class SynchronizationActionHandler : ISynchronizationActionHandler
private readonly ISynchronizationApiClient _synchronizationApiClient;
private readonly IFileDatesSetter _fileDatesSetter;
private readonly ILogger _logger;
-
+
public SynchronizationActionHandler(ISessionService sessionService, IConnectionService connectionService, IDeltaManager deltaManager,
ISynchronizationActionServerInformer synchronizationActionServerInformer,
ISynchronizationActionRemoteUploader synchronizationActionRemoteUploader,
@@ -43,20 +43,20 @@ public SynchronizationActionHandler(ISessionService sessionService, IConnectionS
_fileDatesSetter = fileDatesSetter;
_logger = logger;
}
-
+
public ByteSyncEndpoint CurrentEndPoint => _connectionService.CurrentEndPoint!;
-
+
public async Task RunSynchronizationAction(SharedActionsGroup sharedActionsGroup, CancellationToken cancellationToken = default)
{
try
{
cancellationToken.ThrowIfCancellationRequested();
-
- if (sharedActionsGroup.IsSynchronizeContentOnly || sharedActionsGroup.IsFinallySynchronizeContentAndDate)
+
+ if (sharedActionsGroup.IsCopyContentOnly || sharedActionsGroup.IsFinallyCopyContentAndDate)
{
await RunCopyContentSynchronizationAction(sharedActionsGroup, cancellationToken);
}
- else if (sharedActionsGroup.IsSynchronizeDate || sharedActionsGroup.IsFinallySynchronizeDate)
+ else if (sharedActionsGroup.IsCopyDates || sharedActionsGroup.IsFinallySynchronizeDate)
{
await RunCopyDateSynchronizationAction(sharedActionsGroup, cancellationToken);
}
@@ -76,70 +76,70 @@ public async Task RunSynchronizationAction(SharedActionsGroup sharedActionsGroup
catch (OperationCanceledException)
{
_logger.LogInformation("SynchronizationAction cancelled for {ActionsGroupId}", sharedActionsGroup.ActionsGroupId);
-
+
throw;
}
catch (Exception)
{
await _synchronizationActionServerInformer.HandleCloudActionError(sharedActionsGroup);
-
+
throw;
}
}
-
+
public async Task RunPendingSynchronizationActions(CancellationToken cancellationToken = default)
{
_logger.LogInformation("Running pending synchronization actions");
-
+
cancellationToken.ThrowIfCancellationRequested();
-
+
if (_sessionService.CurrentSession is CloudSession)
{
if (_synchronizationService.SynchronizationProcessData.SynchronizationAbortRequest.Value == null)
{
await _synchronizationActionRemoteUploader.Complete();
-
+
await _synchronizationActionServerInformer.HandlePendingActions();
}
else
{
await _synchronizationActionRemoteUploader.Abort();
-
+
await _synchronizationActionServerInformer.ClearPendingActions();
}
}
}
-
+
private async Task RunCopyContentSynchronizationAction(SharedActionsGroup sharedActionsGroup, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
-
+
var localTargets = GetLocalTargets(sharedActionsGroup);
if (localTargets.Count > 0)
{
await RunCopyContentLocal(sharedActionsGroup, localTargets, cancellationToken);
}
-
+
cancellationToken.ThrowIfCancellationRequested();
-
+
var remoteTargets = GetRemoteTargets(sharedActionsGroup);
if (remoteTargets.Count > 0)
{
await _synchronizationActionRemoteUploader.UploadForRemote(sharedActionsGroup);
}
}
-
+
private async Task RunCopyContentLocal(SharedActionsGroup sharedActionsGroup, HashSet localTargets,
CancellationToken cancellationToken)
{
var sourceFullName = sharedActionsGroup.GetSourceFullName();
-
+
foreach (var localTarget in localTargets)
{
cancellationToken.ThrowIfCancellationRequested();
-
+
var destinationFullName = sharedActionsGroup.GetFullName(localTarget);
-
+
long? transferredBytes;
if (sharedActionsGroup.SynchronizationType == SynchronizationTypes.Full)
{
@@ -148,29 +148,29 @@ private async Task RunCopyContentLocal(SharedActionsGroup sharedActionsGroup, Ha
{
_logger.LogInformation("{Type:l}: creating directory {directory}",
$"Synchronization.{sharedActionsGroup.Operator}", destinationFileInfo.Directory);
-
+
destinationFileInfo.Directory.Create();
}
-
+
_logger.LogInformation("{Type:l}: copying from {source} to {destination}",
$"Synchronization.{sharedActionsGroup.Operator}", sourceFullName, destinationFullName);
-
+
// For full copy, the transferred volume equals the file size
transferredBytes = new FileInfo(sourceFullName).Length;
File.Copy(sourceFullName, destinationFullName, true);
-
+
await ApplyDatesFromLocalSource(sharedActionsGroup, destinationFullName);
}
else
{
var deltaFullName = await _deltaManager.BuildDelta(sharedActionsGroup, localTarget, sourceFullName);
-
+
try
{
// For delta copy, the transferred volume equals the delta size
transferredBytes = new FileInfo(deltaFullName).Length;
await _deltaManager.ApplyDelta(destinationFullName, deltaFullName);
-
+
await ApplyDatesFromSharedActionsGroup(sharedActionsGroup, destinationFullName);
}
finally
@@ -179,7 +179,7 @@ private async Task RunCopyContentLocal(SharedActionsGroup sharedActionsGroup, Ha
File.Delete(deltaFullName);
}
}
-
+
var metrics = new Dictionary
{
[sharedActionsGroup.ActionsGroupId] = new()
@@ -187,15 +187,15 @@ private async Task RunCopyContentLocal(SharedActionsGroup sharedActionsGroup, Ha
TransferredBytes = transferredBytes
}
};
-
+
await _synchronizationActionServerInformer.HandleCloudActionDone(sharedActionsGroup, localTarget,
_synchronizationApiClient.AssertLocalCopyIsDone, metrics);
}
}
-
+
private async Task ApplyDatesFromLocalSource(SharedActionsGroup sharedActionsGroup, string destinationFullName)
{
- if (sharedActionsGroup.IsSynchronizeContentOnly)
+ if (sharedActionsGroup.IsCopyContentOnly)
{
await _fileDatesSetter.SetDates(sharedActionsGroup, destinationFullName, null);
}
@@ -205,23 +205,23 @@ private async Task ApplyDatesFromLocalSource(SharedActionsGroup sharedActionsGro
await _fileDatesSetter.SetDates(sharedActionsGroup, destinationFullName, downloadTargetDates);
}
}
-
+
private async Task ApplyDatesFromSharedActionsGroup(SharedActionsGroup sharedActionsGroup, string destinationFullName)
{
DownloadTargetDates? downloadTargetDates = null;
-
- if (sharedActionsGroup.IsSynchronizeContentAndDate || sharedActionsGroup.IsSynchronizeDate)
+
+ if (sharedActionsGroup.IsFullCopy || sharedActionsGroup.IsCopyDates)
{
downloadTargetDates = DownloadTargetDates.FromSharedActionsGroup(sharedActionsGroup);
}
-
+
await _fileDatesSetter.SetDates(sharedActionsGroup, destinationFullName, downloadTargetDates);
}
-
+
private HashSet GetLocalTargets(SharedActionsGroup sharedActionsGroup)
{
var localTargets = new HashSet();
-
+
foreach (var sharedDataPart in sharedActionsGroup.Targets)
{
if (sharedDataPart.ClientInstanceId.Equals(CurrentEndPoint.ClientInstanceId))
@@ -229,14 +229,14 @@ private HashSet GetLocalTargets(SharedActionsGroup sharedActions
localTargets.Add(sharedDataPart);
}
}
-
+
return localTargets;
}
-
+
private HashSet GetRemoteTargets(SharedActionsGroup sharedActionsGroup)
{
var remoteTargets = new HashSet();
-
+
foreach (var sharedDataPart in sharedActionsGroup.Targets)
{
if (!sharedDataPart.ClientInstanceId.Equals(CurrentEndPoint.ClientInstanceId))
@@ -244,24 +244,24 @@ private HashSet GetRemoteTargets(SharedActionsGroup sharedAction
remoteTargets.Add(sharedDataPart);
}
}
-
+
return remoteTargets;
}
-
+
private async Task RunCopyDateSynchronizationAction(SharedActionsGroup sharedActionsGroup, CancellationToken cancellationToken)
{
var localTargets = GetLocalTargets(sharedActionsGroup);
-
+
foreach (var localTarget in localTargets)
{
var destinationPath = sharedActionsGroup.GetFullName(localTarget);
-
+
cancellationToken.ThrowIfCancellationRequested();
-
+
if (File.Exists(destinationPath))
{
await ApplyDatesFromSharedActionsGroup(sharedActionsGroup, destinationPath);
-
+
await _synchronizationActionServerInformer.HandleCloudActionDone(sharedActionsGroup, localTarget,
_synchronizationApiClient.AssertDateIsCopied);
}
@@ -269,26 +269,26 @@ await _synchronizationActionServerInformer.HandleCloudActionDone(sharedActionsGr
{
_logger.LogWarning("{Type:l}: can not apply last write time on {fileInfo}. This file does not exist",
$"Synchronization.{sharedActionsGroup.Operator}", destinationPath);
-
+
await _synchronizationActionServerInformer.HandleCloudActionError(sharedActionsGroup, localTarget);
}
}
}
-
+
private async Task RunDeleteSynchronizationAction(SharedActionsGroup sharedActionsGroup, CancellationToken cancellationToken)
{
var localTargets = GetLocalTargets(sharedActionsGroup);
-
+
foreach (var localTarget in localTargets)
{
var destinationPath = sharedActionsGroup.GetFullName(localTarget);
-
+
cancellationToken.ThrowIfCancellationRequested();
-
+
if (sharedActionsGroup.IsFile && File.Exists(destinationPath))
{
var fileInfo = new FileInfo(destinationPath);
-
+
_logger.LogInformation("{Type:l}: deleting {fileInfo}",
$"Synchronization.{sharedActionsGroup.Operator}", fileInfo.FullName);
fileInfo.Delete();
@@ -296,10 +296,10 @@ private async Task RunDeleteSynchronizationAction(SharedActionsGroup sharedActio
else if (sharedActionsGroup.IsDirectory && Directory.Exists(destinationPath))
{
var directoryInfo = new DirectoryInfo(destinationPath);
-
+
_logger.LogInformation("{Type:l}: deleting {fileInfo}",
$"Synchronization.{sharedActionsGroup.Operator}", directoryInfo.FullName);
-
+
var subFilesCount = directoryInfo.GetFiles("*", SearchOption.AllDirectories).Length;
directoryInfo.Delete(subFilesCount == 0);
}
@@ -308,30 +308,30 @@ private async Task RunDeleteSynchronizationAction(SharedActionsGroup sharedActio
_logger.LogWarning("{Type:l}: {fileInfo} is already missing, will not try to delete it",
$"Synchronization.{sharedActionsGroup.Operator}", destinationPath);
}
-
+
await _synchronizationActionServerInformer.HandleCloudActionDone(sharedActionsGroup, localTarget,
_synchronizationApiClient.AssertFileOrDirectoryIsDeleted);
}
}
-
+
private async Task RunCreateSynchronizationAction(SharedActionsGroup sharedActionsGroup, CancellationToken cancellationToken)
{
var localTargets = GetLocalTargets(sharedActionsGroup);
-
+
foreach (var localTarget in localTargets)
{
var destinationPath = sharedActionsGroup.GetFullName(localTarget);
cancellationToken.ThrowIfCancellationRequested();
-
+
if (sharedActionsGroup.PathIdentity.FileSystemType == FileSystemTypes.Directory)
{
var directoryInfo = new DirectoryInfo(destinationPath);
-
+
if (!directoryInfo.Exists)
{
_logger.LogInformation("{Type:l}: creating {directoryInfo}",
$"Synchronization.{sharedActionsGroup.Operator}", directoryInfo.FullName);
-
+
directoryInfo.Create();
}
else
@@ -339,7 +339,7 @@ private async Task RunCreateSynchronizationAction(SharedActionsGroup sharedActio
_logger.LogInformation("{Type:l}: {directoryInfo} already exists",
$"Synchronization.{sharedActionsGroup.Operator}", directoryInfo.FullName);
}
-
+
await _synchronizationActionServerInformer.HandleCloudActionDone(sharedActionsGroup, localTarget,
_synchronizationApiClient.AssertDirectoryIsCreated);
}
diff --git a/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModel.cs b/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModel.cs
index 1cb33c93..827d918e 100644
--- a/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModel.cs
+++ b/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModel.cs
@@ -21,11 +21,11 @@ public class AtomicActionEditViewModel : BaseAtomicEditViewModel
private ObservableAsPropertyHelper _isDestinationToVisible;
private ObservableAsPropertyHelper _isDestinationOnVisible;
private readonly IDataPartIndexer _dataPartIndexer;
-
+
public AtomicActionEditViewModel()
{
}
-
+
public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDeleteButton, List? comparisonItems,
IDataPartIndexer dataPartIndexer)
{
@@ -34,7 +34,7 @@ public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDelet
FileSystemType = fileSystemTypes;
ShowDeleteButton = showDeleteButton;
ComparisonItems = comparisonItems;
-
+
Actions = new ObservableCollection();
Sources = new ObservableCollection();
Destinations = new ObservableCollection();
@@ -44,10 +44,10 @@ public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDelet
var canSwapSides = this.WhenAnyValue(x => x.SelectedSource, x => x.SelectedDestination,
(source, destination) => source != null && destination != null);
SwapSidesCommand = ReactiveCommand.Create(SwapSides, canSwapSides);
-
+
this.WhenAnyValue(
x => x.SelectedAction,
- (selectedAction) => selectedAction != null
+ (selectedAction) => selectedAction != null
&& !selectedAction.ActionOperatorType
.In(ActionOperatorTypes.DoNothing, ActionOperatorTypes.Delete, ActionOperatorTypes.Create))
.ObserveOn(RxApp.MainThreadScheduler)
@@ -61,9 +61,9 @@ public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDelet
this.WhenAnyValue(
x => x.SelectedAction,
- (selectedAction) => selectedAction != null &&
- selectedAction.ActionOperatorType.In(ActionOperatorTypes.SynchronizeContentAndDate, ActionOperatorTypes.SynchronizeContentOnly,
- ActionOperatorTypes.SynchronizeDate))
+ (selectedAction) => selectedAction != null &&
+ selectedAction.ActionOperatorType.In(ActionOperatorTypes.Copy, ActionOperatorTypes.CopyContentOnly,
+ ActionOperatorTypes.CopyDatesOnly))
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.IsDestinationToVisible, out _isDestinationToVisible);
@@ -74,12 +74,9 @@ public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDelet
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.IsDestinationOnVisible, out _isDestinationOnVisible);
-
+
this.WhenAnyValue(x => x.SelectedSource)
- .Subscribe(_ =>
- {
- FillDestinations();
- });
+ .Subscribe(_ => { FillDestinations(); });
this.WhenAnyValue(x => x.SelectedAction,
(selectedAction) => selectedAction != null && selectedAction.ActionOperatorType.In(ActionOperatorTypes.Delete))
@@ -89,31 +86,31 @@ public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDelet
SelectedDestination = null;
FillDestinations();
});
-
+
FillActions();
FillSources();
FillDestinations();
}
-
+
public List? ComparisonItems { get; set; }
-
+
internal ObservableCollection Actions { get; set; }
-
+
internal ObservableCollection Sources { get; set; }
-
+
internal ObservableCollection Destinations { get; set; }
private FileSystemTypes FileSystemType { get; }
-
+
[Reactive]
internal ActionViewModel? SelectedAction { get; set; }
-
+
[Reactive]
internal DataPart? SelectedSource { get; set; }
-
+
[Reactive]
internal DataPart? SelectedDestination { get; set; }
-
+
public bool IsSourceVisible => _isSourceVisible.Value;
public bool IsDestinationVisible => _isDestinationVisible.Value;
@@ -121,27 +118,27 @@ public AtomicActionEditViewModel(FileSystemTypes fileSystemTypes, bool showDelet
public bool IsDestinationToVisible => _isDestinationToVisible.Value;
public bool IsDestinationOnVisible => _isDestinationOnVisible.Value;
-
+
[Reactive]
public bool ShowDeleteButton { get; set; }
-
+
public ReactiveCommand RemoveCommand { get; set; }
public ReactiveCommand SwapSidesCommand { get; set; }
-
+
private void FillActions()
{
ActionViewModel actionViewModel;
if (FileSystemType == FileSystemTypes.File)
{
- actionViewModel = new ActionViewModel(ActionOperatorTypes.SynchronizeContentAndDate, Resources.AtomicActionEdit_SynchronizeContentAndDate);
+ actionViewModel = new ActionViewModel(ActionOperatorTypes.Copy, Resources.AtomicActionEdit_Copy);
Actions.Add(actionViewModel);
-
- actionViewModel = new ActionViewModel(ActionOperatorTypes.SynchronizeContentOnly, Resources.AtomicActionEdit_SynchronizeContent);
+
+ actionViewModel = new ActionViewModel(ActionOperatorTypes.CopyContentOnly, Resources.AtomicActionEdit_CopyContent);
Actions.Add(actionViewModel);
-
- actionViewModel = new ActionViewModel(ActionOperatorTypes.SynchronizeDate, Resources.AtomicActionEdit_SynchronizeDate);
+
+ actionViewModel = new ActionViewModel(ActionOperatorTypes.CopyDatesOnly, Resources.AtomicActionEdit_CopyDate);
Actions.Add(actionViewModel);
}
else if (FileSystemType == FileSystemTypes.Directory)
@@ -157,40 +154,41 @@ private void FillActions()
// Actions communes
actionViewModel = new ActionViewModel(ActionOperatorTypes.Delete, Resources.AtomicActionEdit_Delete);
Actions.Add(actionViewModel);
-
+
actionViewModel = new ActionViewModel(ActionOperatorTypes.DoNothing, Resources.AtomicActionEdit_DoNothing);
Actions.Add(actionViewModel);
}
-
+
private void FillSources()
{
Sources.Clear();
Sources.AddAll(_dataPartIndexer.GetAllDataParts());
}
-
+
private void FillDestinations()
{
var selectedDestination = SelectedDestination;
-
+
var destinations = _dataPartIndexer.GetAllDataParts().ToHashSet();
if (SelectedSource != null)
{
destinations.Remove(SelectedSource);
}
-
+
Destinations.Clear();
Destinations.AddAll(destinations);
-
+
if (selectedDestination != null)
{
SelectedDestination = Destinations.FirstOrDefault(ad => ad.Equals(selectedDestination));
}
+
if (SelectedDestination == null && SelectedSource != null && Destinations.Count == 1)
{
SelectedDestination = Destinations.First();
}
-
- if (SelectedDestination == null && ComparisonItems != null &&
+
+ if (SelectedDestination == null && ComparisonItems != null &&
SelectedAction != null && SelectedAction.ActionOperatorType.In(ActionOperatorTypes.Create, ActionOperatorTypes.Delete))
{
// We look if only one destination can be determined
@@ -200,43 +198,43 @@ private void FillDestinations()
foreach (var dataPart in Destinations)
{
var contentIdentities = comparisonItem.GetContentIdentities(dataPart.GetApplicableInventoryPart());
-
- if (SelectedAction is { ActionOperatorType: ActionOperatorTypes.Create }
+
+ if (SelectedAction is { ActionOperatorType: ActionOperatorTypes.Create }
&& contentIdentities.Count == 0)
{
possibleDataParts.Add(dataPart);
}
-
- if (SelectedAction is { ActionOperatorType: ActionOperatorTypes.Delete }
+
+ if (SelectedAction is { ActionOperatorType: ActionOperatorTypes.Delete }
&& contentIdentities.Count != 0)
{
possibleDataParts.Add(dataPart);
}
}
}
-
+
if (possibleDataParts.Count == 1)
{
SelectedDestination = possibleDataParts.Single();
}
}
}
-
+
internal AtomicAction? ExportSynchronizationAction()
{
if (SelectedAction == null)
{
return null;
}
-
- if (SelectedAction.ActionOperatorType.In(ActionOperatorTypes.Create, ActionOperatorTypes.Delete)
+
+ if (SelectedAction.ActionOperatorType.In(ActionOperatorTypes.Create, ActionOperatorTypes.Delete)
&& SelectedDestination == null)
{
return null;
}
if (SelectedAction.ActionOperatorType
- .In(ActionOperatorTypes.SynchronizeDate, ActionOperatorTypes.SynchronizeContentOnly, ActionOperatorTypes.SynchronizeContentAndDate)
+ .In(ActionOperatorTypes.CopyDatesOnly, ActionOperatorTypes.CopyContentOnly, ActionOperatorTypes.Copy)
&& (SelectedSource == null || SelectedDestination == null))
{
return null;
@@ -245,23 +243,23 @@ private void FillDestinations()
var id = $"AAID_{Guid.NewGuid()}";
var atomicAction = new AtomicAction();
atomicAction.AtomicActionId = id;
-
+
atomicAction.Operator = SelectedAction.ActionOperatorType;
atomicAction.Source = SelectedSource;
atomicAction.Destination = SelectedDestination;
-
+
return atomicAction;
}
-
+
internal void SetAtomicAction(AtomicAction atomicAction)
{
SelectedAction = Actions.FirstOrDefault(a => Equals(a.ActionOperatorType, atomicAction.Operator));
-
+
SelectedSource = atomicAction.Source;
SelectedDestination = atomicAction.Destination;
}
-
-
+
+
private void Remove()
{
RaiseRemoveRequested();
@@ -271,7 +269,7 @@ private void SwapSides()
{
var source = SelectedSource;
var destination = SelectedDestination;
-
+
SelectedSource = destination;
SelectedDestination = source;
}
diff --git a/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModel.cs b/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModel.cs
index 91fce2ad..0618602c 100644
--- a/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModel.cs
+++ b/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModel.cs
@@ -6,7 +6,6 @@
using ByteSync.Assets.Resources;
using ByteSync.Business.Actions.Local;
using ByteSync.Business.Sessions;
-using ByteSync.Interfaces;
using ByteSync.Interfaces.Controls.Synchronizations;
using ByteSync.Interfaces.Dialogs;
using ByteSync.Interfaces.Factories.ViewModels;
@@ -16,7 +15,6 @@
using ByteSync.ViewModels.Sessions.Comparisons.Actions.Misc;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
-using Serilog;
namespace ByteSync.ViewModels.Sessions.Comparisons.Actions;
@@ -27,32 +25,35 @@ public class SynchronizationRuleGlobalViewModel : FlyoutElementViewModel
private readonly ILocalizationService _localizationService = null!;
private readonly IActionEditViewModelFactory _actionEditViewModelFactory = null!;
private readonly ISynchronizationRulesService _synchronizationRulesService = null!;
-
- public SynchronizationRuleGlobalViewModel()
+ private readonly ILogger _logger = null!;
+
+ public SynchronizationRuleGlobalViewModel()
{
}
- public SynchronizationRuleGlobalViewModel(IDialogService dialogService,
- ISessionService sessionService, ILocalizationService localizationService, IActionEditViewModelFactory actionEditViewModelFactory,
- ISynchronizationRulesService synchronizationRulesService, SynchronizationRule? baseAutomaticAction, bool isCloneMode)
+ public SynchronizationRuleGlobalViewModel(IDialogService dialogService,
+ ISessionService sessionService, ILocalizationService localizationService, IActionEditViewModelFactory actionEditViewModelFactory,
+ ISynchronizationRulesService synchronizationRulesService, ILogger logger,
+ SynchronizationRule? baseAutomaticAction, bool isCloneMode)
{
_dialogService = dialogService;
_sessionService = sessionService;
_localizationService = localizationService;
_actionEditViewModelFactory = actionEditViewModelFactory;
_synchronizationRulesService = synchronizationRulesService;
-
+ _logger = logger;
+
ShowFileSystemTypeSelection = _sessionService.CurrentSessionSettings!.DataType == DataTypes.FilesDirectories;
-
+
Conditions = new ObservableCollection();
Actions = new ObservableCollection();
-
+
AddConditionCommand = ReactiveCommand.Create(AddCondition);
AddActionCommand = ReactiveCommand.Create(AddAction);
SaveCommand = ReactiveCommand.Create(Save);
ResetCommand = ReactiveCommand.Create(Reset);
CancelCommand = ReactiveCommand.Create(Cancel);
-
+
FileSystemTypes = BuildFileSystemTypes();
SelectedFileSystemType =
_sessionService.CurrentSessionSettings!.DataType == DataTypes.Directories
@@ -61,16 +62,16 @@ public SynchronizationRuleGlobalViewModel(IDialogService dialogService,
ConditionModes = BuildConditionModes();
SelectedConditionMode = ConditionModes.Single(cm => cm.IsAll);
-
+
BaseAutomaticAction = baseAutomaticAction;
IsCloneMode = isCloneMode;
-
+
Reset();
-
+
this.WhenAnyValue(x => x.SelectedFileSystemType)
.Skip(1)
.Subscribe(_ => Reset());
-
+
this.WhenAnyValue(x => x.SelectedConditionMode)
.Subscribe(x =>
{
@@ -92,30 +93,34 @@ public SynchronizationRuleGlobalViewModel(IDialogService dialogService,
.DisposeWith(disposables);
});
}
-
+
public ReactiveCommand AddConditionCommand { get; set; } = null!;
+
public ReactiveCommand AddActionCommand { get; set; } = null!;
+
public ReactiveCommand SaveCommand { get; set; } = null!;
+
public ReactiveCommand ResetCommand { get; set; } = null!;
+
public ReactiveCommand CancelCommand { get; set; } = null!;
-
+
public ObservableCollection FileSystemTypes { get; set; } = null!;
-
+
public ObservableCollection ConditionModes { get; set; } = null!;
-
+
public ObservableCollection Conditions { get; } = null!;
-
+
public ObservableCollection Actions { get; } = null!;
-
+
[Reactive]
public FileSystemTypeViewModel SelectedFileSystemType { get; set; } = null!;
-
+
[Reactive]
public ConditionModeViewModel SelectedConditionMode { get; set; } = null!;
-
+
[Reactive]
public string TextAfterConditionModesComboBox { get; set; } = null!;
-
+
[Reactive]
public bool ShowFileSystemTypeSelection { get; set; }
@@ -124,7 +129,7 @@ public SynchronizationRuleGlobalViewModel(IDialogService dialogService,
[Reactive]
public string SaveWarning { get; set; } = null!;
-
+
public SynchronizationRule? BaseAutomaticAction { get; }
public bool IsCloneMode { get; }
@@ -132,51 +137,55 @@ public SynchronizationRuleGlobalViewModel(IDialogService dialogService,
private ObservableCollection BuildFileSystemTypes()
{
var fileSystemTypes = new ObservableCollection();
-
- var file = new FileSystemTypeViewModel {
- FileSystemType = Common.Business.Inventories.FileSystemTypes.File,
- Description = Resources.General_Files};
- var directory = new FileSystemTypeViewModel {
- FileSystemType = Common.Business.Inventories.FileSystemTypes.Directory,
- Description = Resources.General_Directories};
+ var file = new FileSystemTypeViewModel
+ {
+ FileSystemType = Common.Business.Inventories.FileSystemTypes.File,
+ Description = Resources.General_Files
+ };
+
+ var directory = new FileSystemTypeViewModel
+ {
+ FileSystemType = Common.Business.Inventories.FileSystemTypes.Directory,
+ Description = Resources.General_Directories
+ };
fileSystemTypes.Add(file);
fileSystemTypes.Add(directory);
-
+
return fileSystemTypes;
}
private ObservableCollection BuildConditionModes()
{
var conditionModes = new ObservableCollection();
-
+
var any = new ConditionModeViewModel
{
- Mode = ByteSync.Business.Comparisons.ConditionModes.Any,
+ Mode = Business.Comparisons.ConditionModes.Any,
Description = Resources.SynchronizationRulesGlobal_Any
};
-
+
var all = new ConditionModeViewModel
{
- Mode = ByteSync.Business.Comparisons.ConditionModes.All,
+ Mode = Business.Comparisons.ConditionModes.All,
Description = Resources.SynchronizationRulesGlobal_All
};
-
+
conditionModes.Add(any);
conditionModes.Add(all);
-
+
return conditionModes;
}
-
+
private void AddCondition()
{
var atomicConditionEditViewModel = _actionEditViewModelFactory.BuildAtomicConditionEditViewModel(
SelectedFileSystemType.FileSystemType);
-
+
AddCondition(atomicConditionEditViewModel);
}
-
+
private void AddCondition(AtomicConditionEditViewModel atomicConditionEditViewModel)
{
atomicConditionEditViewModel.PropertyChanged += OnConditionOrActionPropertyChanged;
@@ -184,15 +193,15 @@ private void AddCondition(AtomicConditionEditViewModel atomicConditionEditViewMo
Conditions.Add(atomicConditionEditViewModel);
}
-
+
private void AddAction()
{
var atomicActionEditViewModel = _actionEditViewModelFactory.BuildAtomicActionEditViewModel(
SelectedFileSystemType.FileSystemType, true);
-
+
AddAction(atomicActionEditViewModel);
}
-
+
private void AddAction(AtomicActionEditViewModel atomicActionEditViewModel)
{
atomicActionEditViewModel.PropertyChanged += OnConditionOrActionPropertyChanged;
@@ -200,19 +209,20 @@ private void AddAction(AtomicActionEditViewModel atomicActionEditViewModel)
Actions.Add(atomicActionEditViewModel);
}
-
+
private void Save()
{
try
{
var synchronizationRule = Export();
-
+
if (synchronizationRule != null)
{
ShowWarning = false;
_synchronizationRulesService.AddOrUpdateSynchronizationRule(synchronizationRule);
-
+ LogSaveSuccess(synchronizationRule);
+
_dialogService.CloseFlyout();
}
else
@@ -222,24 +232,25 @@ private void Save()
}
catch (Exception ex)
{
- Log.Error(ex, "Error during saving");
+ _logger.LogError(ex, "Error while saving synchronization rule");
}
}
-
+
private void ShowMissingFieldsWarning()
{
ShowWarning = true;
SaveWarning = Resources.TargetedActionEditionGlobal_MissingFields;
+ _logger.LogWarning("Synchronization rule validation failed: missing fields");
}
-
+
private SynchronizationRule? Export()
{
var synchronizationRule = new SynchronizationRule(SelectedFileSystemType.FileSystemType, SelectedConditionMode.Mode);
- if (BaseAutomaticAction != null && ! IsCloneMode)
+ if (BaseAutomaticAction != null && !IsCloneMode)
{
synchronizationRule.SynchronizationRuleId = BaseAutomaticAction.SynchronizationRuleId;
}
-
+
var isMissingField = false;
// Conditions
@@ -255,12 +266,12 @@ private void ShowMissingFieldsWarning()
isMissingField = true;
}
}
-
+
// Actions
foreach (var automaticActionsActionEditViewModel in Actions)
{
var atomicAction = automaticActionsActionEditViewModel.ExportSynchronizationAction();
-
+
if (atomicAction != null)
{
synchronizationRule.AddAction(atomicAction);
@@ -270,7 +281,7 @@ private void ShowMissingFieldsWarning()
isMissingField = true;
}
}
-
+
if (isMissingField)
{
return null;
@@ -280,7 +291,7 @@ private void ShowMissingFieldsWarning()
return synchronizationRule;
}
}
-
+
private void Reset()
{
ShowWarning = false;
@@ -294,7 +305,7 @@ private void Reset()
ResetToEdition();
}
}
-
+
private void ResetToCreation()
{
Conditions.Clear();
@@ -303,23 +314,24 @@ private void ResetToCreation()
AddCondition();
AddAction();
}
-
-
+
+
private void ResetToEdition()
{
Conditions.Clear();
Actions.Clear();
-
+
SelectedFileSystemType = FileSystemTypes.First(fst => Equals(fst.FileSystemType, BaseAutomaticAction!.FileSystemType));
SelectedConditionMode = ConditionModes.First(cm => Equals(cm.Mode, BaseAutomaticAction!.ConditionMode));
-
+
foreach (var condition in BaseAutomaticAction!.Conditions)
{
- var atomicConditionEditViewModel = _actionEditViewModelFactory.BuildAtomicConditionEditViewModel(SelectedFileSystemType.FileSystemType, condition);
+ var atomicConditionEditViewModel =
+ _actionEditViewModelFactory.BuildAtomicConditionEditViewModel(SelectedFileSystemType.FileSystemType, condition);
AddCondition(atomicConditionEditViewModel);
}
-
+
foreach (var action in BaseAutomaticAction.Actions)
{
var automaticActionsActionEditViewModel = _actionEditViewModelFactory.BuildAtomicActionEditViewModel(
@@ -328,7 +340,7 @@ private void ResetToEdition()
AddAction(automaticActionsActionEditViewModel);
}
}
-
+
private void OnConditionOrActionPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
ShowWarning = false;
@@ -353,18 +365,30 @@ private void OnConditionRemoveRequested(object? sender, BaseAtomicEditViewModel
Conditions.Remove(atomicConditionEditViewModel);
}
-
+
private void Cancel()
{
_dialogService.CloseFlyout();
-
+
Reset();
}
private void OnLocaleChanged()
{
ConditionModes.Single(cm => cm.IsAny).Description = Resources.SynchronizationRulesGlobal_Any;
-
+
ConditionModes.Single(cm => cm.IsAll).Description = Resources.SynchronizationRulesGlobal_All;
}
+
+ private void LogSaveSuccess(SynchronizationRule synchronizationRule)
+ {
+ _logger.LogInformation(
+ "Synchronization rule saved. RuleId={RuleId} FileSystemType={FileSystemType} ConditionMode={ConditionMode} Conditions={ConditionsCount} Actions={ActionsCount} IsCloneMode={IsCloneMode}",
+ synchronizationRule.SynchronizationRuleId ?? string.Empty,
+ synchronizationRule.FileSystemType,
+ synchronizationRule.ConditionMode,
+ synchronizationRule.Conditions.Count,
+ synchronizationRule.Actions.Count,
+ IsCloneMode);
+ }
}
\ No newline at end of file
diff --git a/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModel.cs b/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModel.cs
index 9b975052..fd5e232d 100644
--- a/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModel.cs
+++ b/src/ByteSync.Client/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModel.cs
@@ -7,14 +7,12 @@
using ByteSync.Business.Actions.Local;
using ByteSync.Business.Comparisons;
using ByteSync.Common.Business.Inventories;
-using ByteSync.Interfaces;
using ByteSync.Interfaces.Controls.Comparisons;
using ByteSync.Interfaces.Dialogs;
using ByteSync.Interfaces.Factories.ViewModels;
using ByteSync.Interfaces.Services.Localizations;
using ByteSync.Models.Comparisons.Result;
using ByteSync.ViewModels.Misc;
-using ByteSync.Services.Localizations;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -23,27 +21,28 @@ namespace ByteSync.ViewModels.Sessions.Comparisons.Actions;
public class TargetedActionGlobalViewModel : FlyoutElementViewModel
{
readonly ObservableAsPropertyHelper _canEditAction = null!;
-
+
private readonly ILocalizationService _localizationService = null!;
private readonly IDialogService _dialogService = null!;
private readonly ITargetedActionsService _targetedActionsService = null!;
private readonly IAtomicActionConsistencyChecker _atomicActionConsistencyChecker = null!;
private readonly IActionEditViewModelFactory _actionEditViewModelFactory = null!;
private readonly IAtomicActionValidationFailureReasonService _failureReasonLocalizer = null!;
-
- public TargetedActionGlobalViewModel()
+ private readonly ILogger _logger = null!;
+
+ public TargetedActionGlobalViewModel()
{
-
}
-
- public TargetedActionGlobalViewModel(List comparisonItems,
+
+ public TargetedActionGlobalViewModel(List comparisonItems,
IDialogService dialogService, ILocalizationService localizationService,
ITargetedActionsService targetedActionsService, IAtomicActionConsistencyChecker atomicActionConsistencyChecker,
- IActionEditViewModelFactory actionEditViewModelFactory,
+ IActionEditViewModelFactory actionEditViewModelFactory,
+ ILogger logger,
IAtomicActionValidationFailureReasonService failureReasonLocalizer)
{
ComparisonItems = comparisonItems;
-
+
FileSystemType = ComparisonItems.Select(civm => civm.FileSystemType).ToHashSet().Single();
_dialogService = dialogService;
@@ -52,38 +51,39 @@ public TargetedActionGlobalViewModel(List comparisonItems,
_atomicActionConsistencyChecker = atomicActionConsistencyChecker;
_actionEditViewModelFactory = actionEditViewModelFactory;
_failureReasonLocalizer = failureReasonLocalizer;
-
+ _logger = logger;
+
// Initialize localized messages
ActionIssuesHeaderMessage = _localizationService[nameof(Resources.TargetedActionEditionGlobal_ActionIssues)];
AffectedItemsTooltipHeader = _localizationService[nameof(Resources.TargetedActionEditionGlobal_AffectedItemsTooltip)];
-
+
Actions = new ObservableCollection();
-
+
var canSave = this
.WhenAnyValue(
- x => x.ShowWarning, x=> x.ShowSaveValidItemsCommand,
+ x => x.ShowWarning, x => x.ShowSaveValidItemsCommand,
(showWarning, showSaveValidItemsCommand) => !showWarning || !showSaveValidItemsCommand)
.ObserveOn(RxApp.MainThreadScheduler);
-
+
canSave
.ToProperty(this, x => x.CanEditAction, out _canEditAction);
-
+
this
.WhenAnyValue(x => x.AreMissingFields, x => x.IsInconsistentWithValidItems, x => x.IsInconsistentWithNoValidItems,
(areMissingFields, isInconsistentWithValidItems, isInconsistentWithNoValidItems) =>
areMissingFields || isInconsistentWithValidItems != null || isInconsistentWithNoValidItems)
.ObserveOn(RxApp.MainThreadScheduler)
.ToPropertyEx(this, x => x.ShowWarning);
-
+
AddActionCommand = ReactiveCommand.Create(AddAction);
SaveCommand = ReactiveCommand.Create(Save, canSave);
ResetCommand = ReactiveCommand.Create(Reset);
CancelCommand = ReactiveCommand.Create(Cancel);
-
+
SaveValidItemsCommand = ReactiveCommand.Create(SaveValidItems);
-
+
ResetWarning();
-
+
Reset();
this.WhenActivated(disposables =>
@@ -94,29 +94,32 @@ public TargetedActionGlobalViewModel(List comparisonItems,
.DisposeWith(disposables);
});
}
-
+
public List ComparisonItems { get; private set; } = null!;
-
+
public ReactiveCommand AddActionCommand { get; set; } = null!;
-
+
public ReactiveCommand SaveCommand { get; set; } = null!;
+
public ReactiveCommand ResetCommand { get; set; } = null!;
+
public ReactiveCommand CancelCommand { get; set; } = null!;
-
+
public ReactiveCommand SaveValidItemsCommand { get; set; } = null!;
-
+
internal AtomicAction? BaseAtomicAction { get; set; }
+
internal ObservableCollection Actions { get; } = null!;
-
+
private FileSystemTypes FileSystemType { get; }
- public extern bool ShowWarning { [ObservableAsProperty]get; }
-
+ public extern bool ShowWarning { [ObservableAsProperty] get; }
+
[Reactive]
public bool ShowSaveValidItemsCommand { get; set; }
-
+
public bool CanEditAction => _canEditAction.Value;
-
+
[Reactive]
public string SaveWarning { get; set; } = string.Empty;
@@ -131,32 +134,32 @@ public TargetedActionGlobalViewModel(List comparisonItems,
public ObservableCollection FailureSummaries { get; set; } = new();
- [Reactive]
+ [Reactive]
public string ActionIssuesHeaderMessage { get; set; } = string.Empty;
- [Reactive]
+ [Reactive]
public string AffectedItemsTooltipHeader { get; set; } = string.Empty;
-
+
private void AddAction()
{
- var atomicActionEditViewModel =
+ var atomicActionEditViewModel =
_actionEditViewModelFactory.BuildAtomicActionEditViewModel(FileSystemType, false, null, ComparisonItems);
atomicActionEditViewModel.PropertyChanged += AtomicActionEditViewModelOnPropertyChanged;
-
+
Actions.Add(atomicActionEditViewModel);
}
-
+
private void AtomicActionEditViewModelOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
ResetWarning();
}
-
-
+
+
private void Save()
{
var atomicAction = Export();
-
+
if (atomicAction == null)
{
ShowMissingFieldsWarning();
@@ -170,16 +173,17 @@ private void Save()
ResetWarning();
_targetedActionsService.AddTargetedAction(atomicAction, ComparisonItems);
-
+ LogConsistencySuccess(atomicAction, ComparisonItems);
+
_dialogService.CloseFlyout();
}
else
{
- ShowConsistencyWarning(result);
+ ShowConsistencyWarning(atomicAction, result);
}
}
}
-
+
private void SaveValidItems()
{
var atomicAction = Export();
@@ -192,18 +196,19 @@ private void SaveValidItems()
var result = _atomicActionConsistencyChecker.CheckCanAdd(atomicAction, ComparisonItems);
ResetWarning();
-
+
_targetedActionsService.AddTargetedAction(atomicAction, result.GetValidComparisonItems());
-
+ LogConsistencySuccess(atomicAction, result.GetValidComparisonItems());
+
_dialogService.CloseFlyout();
}
}
-
+
private AtomicAction? Export()
{
var automaticActionsActionEditViewModel = Actions.Single(); // (AutomaticActionsActionEditViewModel)region.Views.Single();
var atomicAction = automaticActionsActionEditViewModel.ExportSynchronizationAction();
-
+
if (atomicAction != null)
{
if (BaseAtomicAction != null)
@@ -211,11 +216,11 @@ private void SaveValidItems()
atomicAction.AtomicActionId = BaseAtomicAction.AtomicActionId;
}
}
-
-
+
+
return atomicAction;
}
-
+
private void Reset()
{
if (BaseAtomicAction == null)
@@ -226,25 +231,26 @@ private void Reset()
{
ResetToEdition();
}
-
+
ResetWarning();
}
-
+
private void ResetToCreation()
{
ClearActions();
-
+
AddAction();
}
-
+
private void ResetToEdition()
{
ClearActions();
-
- var atomicActionEditViewModel = _actionEditViewModelFactory.BuildAtomicActionEditViewModel(FileSystemType, false, BaseAtomicAction, ComparisonItems);
-
+
+ var atomicActionEditViewModel =
+ _actionEditViewModelFactory.BuildAtomicActionEditViewModel(FileSystemType, false, BaseAtomicAction, ComparisonItems);
+
atomicActionEditViewModel.PropertyChanged += AtomicActionEditViewModelOnPropertyChanged;
-
+
atomicActionEditViewModel.SetAtomicAction(BaseAtomicAction!);
Actions.Add(atomicActionEditViewModel);
@@ -259,11 +265,11 @@ private void ClearActions()
Actions.Clear();
}
-
+
private void Cancel()
{
RaiseCloseFlyoutRequested();
-
+
Reset();
}
@@ -288,17 +294,17 @@ private void ShowMissingFieldsWarning()
IsInconsistentWithValidItems = null;
IsInconsistentWithNoValidItems = false;
FailureSummaries.Clear(); // Clear previous validation failure summaries
-
+
DoShowWarning();
}
- private void ShowConsistencyWarning(AtomicActionConsistencyCheckCanAddResult checkCanAddResult)
+ private void ShowConsistencyWarning(AtomicAction atomicAction, AtomicActionConsistencyCheckCanAddResult checkCanAddResult)
{
ShowSaveValidItemsCommand = checkCanAddResult.GetValidComparisonItems().Count > 0;
-
+
if (ShowSaveValidItemsCommand)
{
- IsInconsistentWithValidItems = new Tuple(checkCanAddResult.GetValidComparisonItems().Count,
+ IsInconsistentWithValidItems = new Tuple(checkCanAddResult.GetValidComparisonItems().Count,
checkCanAddResult.GetInvalidComparisonItems().Count);
IsInconsistentWithNoValidItems = false;
}
@@ -312,25 +318,28 @@ private void ShowConsistencyWarning(AtomicActionConsistencyCheckCanAddResult che
FailureSummaries.Clear();
var summaries = checkCanAddResult.FailedValidations
.GroupBy(f => f.FailureReason!.Value)
- .Select(g => new ValidationFailureSummary
+ .Select(g => new ValidationFailureSummary
{
Reason = g.Key,
Count = g.Count(),
LocalizedMessage = _failureReasonLocalizer.GetLocalizedMessage(g.Key),
AffectedItems = g.Select(f => f.ComparisonItem).ToList()
})
- .OrderByDescending(s => s.Count); // Most frequent failures first
+ .OrderByDescending(s => s.Count)
+ .ToList(); // Most frequent failures first
foreach (var summary in summaries)
{
FailureSummaries.Add(summary);
}
+ LogConsistencyFailure(atomicAction, checkCanAddResult, summaries);
+
AreMissingFields = false;
-
+
DoShowWarning();
}
-
+
private void DoShowWarning()
{
var saveWarning = "";
@@ -349,15 +358,15 @@ private void DoShowWarning()
{
saveWarning = Resources.TargetedActionEditionGlobal_SaveWarningLocked;
}
-
+
SaveWarning = saveWarning;
}
-
+
private void OnAtomicInputChanged()
{
ResetWarning();
}
-
+
private void ResetWarning()
{
AreMissingFields = false;
@@ -365,4 +374,62 @@ private void ResetWarning()
IsInconsistentWithNoValidItems = false;
FailureSummaries.Clear();
}
+
+ private void LogConsistencyFailure(AtomicAction atomicAction, AtomicActionConsistencyCheckCanAddResult result,
+ IEnumerable summaries)
+ {
+ var validItemsCount = result.GetValidComparisonItems().Count;
+ var invalidItemsCount = result.GetInvalidComparisonItems().Count;
+ var failureDetails = BuildFailureDetails(summaries);
+
+ _logger.LogWarning(
+ "Targeted action validation failed. Operator={Operator} Source={Source} Destination={Destination} FileSystemType={FileSystemType} TotalItems={TotalItems} ValidItems={ValidItems} InvalidItems={InvalidItems} Failures={Failures}",
+ atomicAction.Operator,
+ atomicAction.SourceName ?? string.Empty,
+ atomicAction.DestinationName ?? string.Empty,
+ FileSystemType,
+ result.ComparisonItems.Count,
+ validItemsCount,
+ invalidItemsCount,
+ failureDetails);
+ }
+
+ private void LogConsistencySuccess(AtomicAction atomicAction, IEnumerable comparisonItems)
+ {
+ var itemList = BuildItemList(comparisonItems);
+
+ _logger.LogInformation(
+ "Targeted action created. Operator={Operator} Source={Source} Destination={Destination} FileSystemType={FileSystemType} Items={Items}",
+ atomicAction.Operator,
+ atomicAction.SourceName ?? string.Empty,
+ atomicAction.DestinationName ?? string.Empty,
+ FileSystemType,
+ itemList);
+ }
+
+ private static string BuildItemList(IEnumerable comparisonItems)
+ {
+ return string.Join(", ", comparisonItems.Select(item => item.PathIdentity?.LinkingKeyValue ?? "unknown"));
+ }
+
+ private static string BuildFailureDetails(IEnumerable summaries)
+ {
+ const int maxItemsPerReason = 3;
+
+ var details = summaries
+ .Select(summary =>
+ {
+ var items = summary.AffectedItems
+ .Select(item => item.PathIdentity.LinkingKeyValue)
+ .Take(maxItemsPerReason)
+ .ToList();
+
+ var suffix = summary.AffectedItems.Count > maxItemsPerReason ? ", ..." : string.Empty;
+
+ return $"{summary.Reason}={summary.Count} [{string.Join(", ", items)}{suffix}]";
+ })
+ .ToList();
+
+ return details.Count == 0 ? "none" : string.Join("; ", details);
+ }
}
\ No newline at end of file
diff --git a/src/ByteSync.Client/ViewModels/Sessions/Synchronizations/SynchronizationConfirmationViewModel.cs b/src/ByteSync.Client/ViewModels/Sessions/Synchronizations/SynchronizationConfirmationViewModel.cs
index 93baa33c..f931e195 100644
--- a/src/ByteSync.Client/ViewModels/Sessions/Synchronizations/SynchronizationConfirmationViewModel.cs
+++ b/src/ByteSync.Client/ViewModels/Sessions/Synchronizations/SynchronizationConfirmationViewModel.cs
@@ -115,8 +115,8 @@ private void ComputeStatistics(List actions)
DestinationCode = dataNode?.Code ?? "?",
MachineName = sessionMember?.MachineName ?? "Unknown",
CreateCount = group.Count(a => a.IsCreate),
- SynchronizeContentCount = group.Count(a => a.IsSynchronizeContent),
- SynchronizeDateCount = group.Count(a => a.IsSynchronizeDate),
+ SynchronizeContentCount = group.Count(a => a.IsCopyContent),
+ SynchronizeDateCount = group.Count(a => a.IsCopyDates),
DeleteCount = group.Count(a => a.IsDelete)
};
diff --git a/src/ByteSync.Common/Business/Actions/AbstractAction.cs b/src/ByteSync.Common/Business/Actions/AbstractAction.cs
index bda437b5..e5b291ba 100644
--- a/src/ByteSync.Common/Business/Actions/AbstractAction.cs
+++ b/src/ByteSync.Common/Business/Actions/AbstractAction.cs
@@ -6,59 +6,38 @@ public abstract class AbstractAction
{
public ActionOperatorTypes Operator { get; set; }
- public bool IsSynchronizeContent
+ public bool IsCopyContent
{
- get
- {
- return Operator.In(ActionOperatorTypes.SynchronizeContentOnly, ActionOperatorTypes.SynchronizeContentAndDate);
- }
+ get { return Operator.In(ActionOperatorTypes.CopyContentOnly, ActionOperatorTypes.Copy); }
}
- public bool IsSynchronizeContentOnly
+ public bool IsCopyContentOnly
{
- get
- {
- return Operator.In(ActionOperatorTypes.SynchronizeContentOnly);
- }
+ get { return Operator.In(ActionOperatorTypes.CopyContentOnly); }
}
- public bool IsSynchronizeContentAndDate
+ public bool IsFullCopy
{
- get
- {
- return Operator.In(ActionOperatorTypes.SynchronizeContentAndDate);
- }
+ get { return Operator.In(ActionOperatorTypes.Copy); }
}
- public bool IsSynchronizeDate
+ public bool IsCopyDates
{
- get
- {
- return Operator.In(ActionOperatorTypes.SynchronizeDate);
- }
+ get { return Operator.In(ActionOperatorTypes.CopyDatesOnly); }
}
public bool IsDelete
{
- get
- {
- return Operator.In(ActionOperatorTypes.Delete);
- }
+ get { return Operator.In(ActionOperatorTypes.Delete); }
}
public bool IsCreate
{
- get
- {
- return Operator.In(ActionOperatorTypes.Create);
- }
+ get { return Operator.In(ActionOperatorTypes.Create); }
}
public bool IsDoNothing
{
- get
- {
- return Operator.In(ActionOperatorTypes.DoNothing);
- }
+ get { return Operator.In(ActionOperatorTypes.DoNothing); }
}
}
\ No newline at end of file
diff --git a/src/ByteSync.Common/Business/Actions/AbstractActionsGroup.cs b/src/ByteSync.Common/Business/Actions/AbstractActionsGroup.cs
index d33f2e57..6585538a 100644
--- a/src/ByteSync.Common/Business/Actions/AbstractActionsGroup.cs
+++ b/src/ByteSync.Common/Business/Actions/AbstractActionsGroup.cs
@@ -7,50 +7,35 @@ public class AbstractActionsGroup : AbstractAction
public string ActionsGroupId { get; set; } = null!;
public long? Size { get; set; }
-
+
public DateTime? CreationTimeUtc { get; set; }
public DateTime? LastWriteTimeUtc { get; set; }
- public bool AppliesOnlySynchronizeDate { get; set; }
+ public bool AppliesOnlyCopyDate { get; set; }
- public bool IsFinallySynchronizeContentAndDate
+ public bool IsFinallyCopyContentAndDate
{
- get
- {
- return IsSynchronizeContentAndDate && !AppliesOnlySynchronizeDate;
- }
+ get { return IsFullCopy && !AppliesOnlyCopyDate; }
}
public bool IsFinallySynchronizeDate
{
- get
- {
- return IsSynchronizeContentAndDate && AppliesOnlySynchronizeDate;
- }
+ get { return IsFullCopy && AppliesOnlyCopyDate; }
}
-
+
public bool IsInitialOperatingOnSourceNeeded
{
- get
- {
- return IsSynchronizeContentOnly || IsFinallySynchronizeContentAndDate;
- }
+ get { return IsCopyContentOnly || IsFinallyCopyContentAndDate; }
}
public bool NeedsOperatingOnSourceAndTargets
{
- get
- {
- return !NeedsOnlyOperatingOnTargets;
- }
+ get { return !NeedsOnlyOperatingOnTargets; }
}
public bool NeedsOnlyOperatingOnTargets
{
- get
- {
- return IsSynchronizeDate || IsCreate || IsDelete || IsFinallySynchronizeDate;
- }
+ get { return IsCopyDates || IsCreate || IsDelete || IsFinallySynchronizeDate; }
}
}
\ No newline at end of file
diff --git a/src/ByteSync.Common/Business/Actions/ActionOperatorTypes.cs b/src/ByteSync.Common/Business/Actions/ActionOperatorTypes.cs
index 4144ba3a..6f2e6f14 100644
--- a/src/ByteSync.Common/Business/Actions/ActionOperatorTypes.cs
+++ b/src/ByteSync.Common/Business/Actions/ActionOperatorTypes.cs
@@ -3,13 +3,13 @@
public enum ActionOperatorTypes
{
// File only
- SynchronizeContentOnly = 1,
- SynchronizeDate = 2,
- SynchronizeContentAndDate = 3,
-
+ CopyContentOnly = 1,
+ CopyDatesOnly = 2,
+ Copy = 3,
+
// Directory only
Create = 5,
-
+
// Common
Delete = 10,
DoNothing = 11
diff --git a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/BaseTestFiltering.cs b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/BaseTestFiltering.cs
index e2bcf8a2..5fb64efa 100644
--- a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/BaseTestFiltering.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/BaseTestFiltering.cs
@@ -25,7 +25,7 @@ public abstract class BaseTestFiltering : IntegrationTest
protected FilterParser _filterParser = null!;
protected ExpressionEvaluatorFactory _evaluatorFactory = null!;
protected ILogger _logger = null!;
-
+
// [SetUp]
protected void SetupBase()
{
@@ -59,14 +59,15 @@ protected void SetupBase()
_evaluatorFactory = Container.Resolve();
_logger = Container.Resolve>();
}
-
+
protected ComparisonItem CreateBasicComparisonItem(FileSystemTypes fileSystemType = FileSystemTypes.File,
string filePath = "/file1.txt", string fileName = "file1.txt")
{
var pathIdentity = new PathIdentity(fileSystemType, filePath, fileName, filePath);
+
return new ComparisonItem(pathIdentity);
}
-
+
protected (FileDescription, InventoryPart) CreateFileDescription(
string inventoryId,
string rootPath,
@@ -75,13 +76,13 @@ protected ComparisonItem CreateBasicComparisonItem(FileSystemTypes fileSystemTyp
long size = 100,
string relativePath = "/unset")
{
- string letter = inventoryId.Replace("Id_", "");
+ var letter = inventoryId.Replace("Id_", "");
var inventory = new Inventory { InventoryId = inventoryId, Code = letter };
- string code = $"{letter}1";
+ var code = $"{letter}1";
var inventoryPart = new InventoryPart(inventory, rootPath, FileSystemTypes.Directory);
inventoryPart.Code = code;
-
+
var fileDescription = new FileDescription
{
InventoryPart = inventoryPart,
@@ -92,7 +93,7 @@ protected ComparisonItem CreateBasicComparisonItem(FileSystemTypes fileSystemTyp
Sha256 = hash,
RelativePath = relativePath
};
-
+
return (fileDescription, inventoryPart);
}
@@ -100,26 +101,26 @@ protected ComparisonItem CreateBasicComparisonItem(FileSystemTypes fileSystemTyp
string inventoryId,
string rootPath)
{
- string letter = inventoryId.Replace("Id_", "");
+ var letter = inventoryId.Replace("Id_", "");
var inventory = new Inventory { InventoryId = inventoryId, Code = letter };
- string code = $"{letter}1";
+ var code = $"{letter}1";
var inventoryPart = new InventoryPart(inventory, rootPath, FileSystemTypes.Directory);
inventoryPart.Code = code;
-
+
var directoryDescription = new DirectoryDescription
{
InventoryPart = inventoryPart,
};
-
+
return (directoryDescription, inventoryPart);
}
-
+
protected void ConfigureDataPartIndex(
Dictionary dataParts)
{
var mockDataPartIndexer = Container.Resolve>();
-
+
foreach (var pair in dataParts)
{
var dataPart = new DataPart(pair.Key, pair.Value.Item1);
@@ -132,7 +133,7 @@ protected void ConfigureDataPartIndex(
Dictionary dataParts)
{
var mockDataPartIndexer = Container.Resolve>();
-
+
foreach (var pair in dataParts)
{
var dataPart = new DataPart(pair.Key, pair.Value.Item1);
@@ -140,7 +141,7 @@ protected void ConfigureDataPartIndex(
.Returns(dataPart);
}
}
-
+
protected ComparisonItem PrepareComparisonWithTwoContents(
string leftDataPartId,
string leftHash,
@@ -148,7 +149,7 @@ protected ComparisonItem PrepareComparisonWithTwoContents(
string rightDataPartId,
string rightHash,
DateTime rightDateTime
- )
+ )
{
return PrepareComparisonWithTwoContents(
leftDataPartId,
@@ -160,7 +161,7 @@ DateTime rightDateTime
rightDateTime,
100);
}
-
+
protected ComparisonItem PrepareComparisonWithTwoContents(
string leftDataPartId,
string leftHash,
@@ -172,21 +173,21 @@ protected ComparisonItem PrepareComparisonWithTwoContents(
long rightSize)
{
var comparisonItem = CreateBasicComparisonItem();
-
+
var (fileDescA, inventoryPartA) = CreateFileDescription(
"Id_A",
"/testRootA",
leftDateTime,
leftHash,
leftSize);
-
+
var (fileDescB, inventoryPartB) = CreateFileDescription(
"Id_B",
"/testRootB",
rightDateTime,
rightHash,
rightSize);
-
+
// Créer et ajouter les identités de contenu
var contentIdCoreA = new ContentIdentityCore
{
@@ -196,7 +197,7 @@ protected ComparisonItem PrepareComparisonWithTwoContents(
var contentIdA = new ContentIdentity(contentIdCoreA);
comparisonItem.AddContentIdentity(contentIdA);
contentIdA.Add(fileDescA);
-
+
if (leftHash == rightHash)
{
contentIdA.Add(fileDescB);
@@ -212,19 +213,19 @@ protected ComparisonItem PrepareComparisonWithTwoContents(
comparisonItem.AddContentIdentity(contentIdB);
contentIdB.Add(fileDescB);
}
-
+
// Configurer l'indexeur de DataPart
var dataParts = new Dictionary
{
{ leftDataPartId, (inventoryPartA, fileDescA) },
{ rightDataPartId, (inventoryPartB, fileDescB) }
};
-
+
ConfigureDataPartIndex(dataParts);
-
+
return comparisonItem;
}
-
+
protected ComparisonItem PrepareComparisonWithOneContent(
string dataPartId,
string leftHash,
@@ -233,9 +234,9 @@ protected ComparisonItem PrepareComparisonWithOneContent(
string fileName = "file1.txt")
{
var comparisonItem = CreateBasicComparisonItem(FileSystemTypes.File, "/" + fileName.TrimStart('/'), fileName);
-
+
var letter = new string(dataPartId.TakeWhile(char.IsLetter).ToArray());
-
+
var (fileDesc, inventoryPart) = CreateFileDescription(
$"Id_{letter}",
$"/testRoot{letter}",
@@ -257,18 +258,18 @@ protected ComparisonItem PrepareComparisonWithOneContent(
{
{ dataPartId, (inventoryPart, fileDesc) }
};
-
+
ConfigureDataPartIndex(dataParts);
-
+
return comparisonItem;
}
protected ComparisonItem PrepareComparisonWithOneDirectory(string dataPartId)
{
var comparisonItem = CreateBasicComparisonItem(FileSystemTypes.Directory);
-
- string letter = dataPartId[0].ToString();
-
+
+ var letter = dataPartId[0].ToString();
+
var (dirDesc, inventoryPart) = CreateDirectoryDescription(
$"Id_{letter}",
$"/testRoot{letter}");
@@ -282,9 +283,9 @@ protected ComparisonItem PrepareComparisonWithOneDirectory(string dataPartId)
{
{ dataPartId, (inventoryPart, dirDesc) }
};
-
+
ConfigureDataPartIndex(dataParts);
-
+
return comparisonItem;
}
@@ -299,6 +300,7 @@ protected bool EvaluateFilterExpression(string filterText, ComparisonItem item)
var expression = parseResult.Expression!;
var evaluator = _evaluatorFactory.GetEvaluator(expression);
+
return evaluator.Evaluate(expression, item);
}
}
\ No newline at end of file
diff --git a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering.cs b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering.cs
index 3833de97..b0f79521 100644
--- a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering.cs
@@ -22,7 +22,7 @@ public void Setup()
public void TestParse_Complete_Expression()
{
// Arrange
- var filterText = "A1.contents==B1.contents";
+ var filterText = "A1.content==B1.content";
ConfigureDataPartIndexer();
@@ -39,7 +39,7 @@ public void TestParse_Complete_Expression()
public void TestParse_Complete_Expression_MultiDataNode()
{
// Arrange
- var filterText = "Aa1.contents==Ba1.contents";
+ var filterText = "Aa1.content==Ba1.content";
ConfigureDataPartIndexer("Aa", "Ba");
@@ -56,7 +56,7 @@ public void TestParse_Complete_Expression_MultiDataNode()
public void TestParse_CompleteLowerCase_Expression()
{
// Arrange
- var filterText = "a1.contents==b1.contents";
+ var filterText = "a1.content==b1.content";
ConfigureDataPartIndexer();
@@ -73,7 +73,7 @@ public void TestParse_CompleteLowerCase_Expression()
public void TestParse_CompleteWithSpaces1_Expression()
{
// Arrange
- var filterText = "A1.contents == B1.contents";
+ var filterText = "A1.content == B1.content";
ConfigureDataPartIndexer();
@@ -107,7 +107,7 @@ public void TestParse_CompleteWithSpaces2_Expression()
public void TestParse_Incomplete_Expression()
{
// Arrange
- var filterText = "A1.contents==";
+ var filterText = "A1.content==";
ConfigureDataPartIndexer();
@@ -156,7 +156,7 @@ public void TestParse_Incomplete_DotExpression()
public void TestParse_Incomplete_Operator()
{
// Arrange
- var filterText = "A1.contents";
+ var filterText = "A1.content";
// Act
var parseResult = _filterParser.TryParse(filterText);
@@ -201,7 +201,7 @@ public void TestParse_Incomplete_NotExpression()
public void TestParse_Incomplete_ParenthesesExpression()
{
// Arrange
- var filterText = "(A1.contents==B1.contents";
+ var filterText = "(A1.content==B1.content";
ConfigureDataPartIndexer();
@@ -218,7 +218,7 @@ public void TestParse_Incomplete_ParenthesesExpression()
public void TestParse_Incomplete_WithAndOperator()
{
// Arrange
- var filterText = "A1.contents==B1.contents AND on:";
+ var filterText = "A1.content==B1.content AND on:";
ConfigureDataPartIndexer();
@@ -255,7 +255,7 @@ public void TestFilterService_HandlesIncompleteExpression_WithoutException()
var comparisonItem = CreateBasicComparisonItem();
// Act & Assert - Should not throw
- var filter = filterService.BuildFilter("A1.content==");
+ var filter = filterService.BuildFilter("A1.contents==");
// By default, incomplete filters should accept everything
filter(comparisonItem).Should().BeTrue();
@@ -271,7 +271,7 @@ public void TestFilterService_BuildFilter_ListWithIncompleteExpressions()
// A complete expression and an incomplete one
var filterTexts = new List
{
- "A1.content==B1.content", // complete
+ "A1.contents==B1.contents", // complete
"A1.content==" // incomplete
};
diff --git a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Actions.cs b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Actions.cs
index 1d06d369..34c18f36 100644
--- a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Actions.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Actions.cs
@@ -28,7 +28,7 @@ public void TestFiltering_Actions_CountGreaterThanZero()
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
@@ -51,7 +51,7 @@ public void TestFiltering_Actions_CountGreaterThanZer_Simplified()
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
@@ -105,136 +105,136 @@ public void TestFiltering_TargetedActions()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.targeted>0";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
-
+
+
[Test]
public void TestFiltering_RuleActions()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.rules>0";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_ActionsByType_Delete()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.delete>0";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_TargetedActionsByType_Delete()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.targeted.delete>0";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_TargetedActionsByType_Delete_Simplified()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.targeted.delete";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_RuleActionsByType_SynchronizeContent()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
- var filterText = "actions.rules.copy-contents>0";
-
+
+ var filterText = "actions.rules.copy-content>0";
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
@@ -244,104 +244,104 @@ public void TestFiltering_RuleActionsByType_SynchronizeContent_Simplified()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
- var filterText = "actions.rules.copy-contents";
-
+
+ var filterText = "actions.rules.copy-content";
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_NoSuchActions()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.targeted.create==0";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_ComplexCondition()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.delete>0 AND actions.create==0";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
[Test]
public void TestFiltering_ComplexCondition_Simplified()
{
// Arrange
var comparisonItem = CreateBasicComparisonItem();
-
+
var actions = new List
{
- CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentOnly, false),
+ CreateAtomicAction(comparisonItem, ActionOperatorTypes.CopyContentOnly, false),
CreateAtomicAction(comparisonItem, ActionOperatorTypes.Delete, true)
};
-
+
_mockActionRepository.AddOrUpdate(actions);
-
+
var filterText = "actions.delete AND NOT actions.create";
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
-
+
private AtomicAction CreateAtomicAction(ComparisonItem comparisonItem, ActionOperatorTypes operatorType, bool isTargeted)
{
var action = new AtomicAction($"AAID_{Guid.NewGuid()}", comparisonItem);
action.Operator = operatorType;
-
+
if (!isTargeted)
{
action.SynchronizationRule = new SynchronizationRule(comparisonItem.FileSystemType, ConditionModes.All);
}
-
+
return action;
}
}
\ No newline at end of file
diff --git a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Contents.cs b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Contents.cs
index 31a7f87b..80164c5b 100644
--- a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Contents.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_Contents.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using Autofac;
using ByteSync.Business;
using ByteSync.Business.Comparisons;
@@ -19,12 +20,12 @@ public void Setup()
{
SetupBase();
}
-
+
[Test]
public void Test_Contents()
{
// Arrange
- var filterText = "A1.contents==B1.contents";
+ var filterText = "A1.content==B1.content";
var pathIdentity = new PathIdentity(FileSystemTypes.File, "/file1.txt", "file1.txt", "/file1.txt");
var comparisonItem = new ComparisonItem(pathIdentity);
@@ -33,7 +34,8 @@ public void Test_Contents()
var inventoryA = new Inventory();
inventoryA.InventoryId = "Id_A";
var inventoryPartA1 = new InventoryPart(inventoryA, "/testRootA1", FileSystemTypes.Directory);
- var fileDescriptionA1 = new FileDescription {
+ var fileDescriptionA1 = new FileDescription
+ {
InventoryPart = inventoryPartA1,
LastWriteTimeUtc = lastWriteTime1,
Size = 100,
@@ -45,7 +47,8 @@ public void Test_Contents()
var inventoryB = new Inventory();
inventoryB.InventoryId = "Id_B";
var inventoryPartB1 = new InventoryPart(inventoryB, "/testRootB1", FileSystemTypes.Directory);
- var fileDescriptionB1 = new FileDescription {
+ var fileDescriptionB1 = new FileDescription
+ {
InventoryPart = inventoryPartB1,
LastWriteTimeUtc = lastWriteTime1,
Size = 100,
@@ -61,7 +64,7 @@ public void Test_Contents()
comparisonItem.AddContentIdentity(contentIdentity);
contentIdentity.Add(fileDescriptionA1);
contentIdentity.Add(fileDescriptionB1);
-
+
var mockDataPartIndexer = Container.Resolve>();
var dataPartA1 = new DataPart("A1", inventoryPartA1);
mockDataPartIndexer.Setup(m => m.GetDataPart("A1"))
@@ -69,10 +72,10 @@ public void Test_Contents()
var dataPartA2 = new DataPart("B1", inventoryPartB1);
mockDataPartIndexer.Setup(m => m.GetDataPart("B1"))
.Returns(dataPartA2);
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().BeTrue();
}
@@ -90,18 +93,18 @@ public void Test_Contents_Simplified(string leftHash, string leftDateTimeStr, st
string @operator, bool expectedResult)
{
// Arrange
- var filterText = $"A1.contents{@operator}B1._";
+ var filterText = $"A1.content{@operator}B1._";
- DateTime leftDateTime = DateTime.Parse(leftDateTimeStr, System.Globalization.CultureInfo.InvariantCulture);
- DateTime rightDateTime = DateTime.Parse(rightDateTimeStr, System.Globalization.CultureInfo.InvariantCulture);
+ var leftDateTime = DateTime.Parse(leftDateTimeStr, CultureInfo.InvariantCulture);
+ var rightDateTime = DateTime.Parse(rightDateTimeStr, CultureInfo.InvariantCulture);
var comparisonItem = PrepareComparisonWithTwoContents(
"A1", leftHash, leftDateTime,
"B1", rightHash, rightDateTime);
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().Be(expectedResult);
}
diff --git a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_ContentsAndDate.cs b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_ContentsAndDate.cs
index 95f21d9d..1dde292a 100644
--- a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_ContentsAndDate.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_ContentsAndDate.cs
@@ -1,4 +1,5 @@
-using FluentAssertions;
+using System.Globalization;
+using FluentAssertions;
namespace ByteSync.Client.IntegrationTests.Business.Filtering;
@@ -9,7 +10,7 @@ public void Setup()
{
SetupBase();
}
-
+
[TestCase("sameHash", "2023-10-01", "sameHash", "2023-10-01", "==", true)]
[TestCase("sameHash", "2023-10-01", "sameHash", "2023-10-01", "<>", false)]
[TestCase("sameHash", "2023-10-01", "sameHash", "2023-10-01", "!=", false)]
@@ -23,18 +24,18 @@ public void Test_ContentsAndDate(string leftHash, string leftDateTimeStr, string
string @operator, bool expectedResult)
{
// Arrange
- var filterText = $"A1.contents-and-date{@operator}B1.contents-and-date";
+ var filterText = $"A1.content-and-date{@operator}B1.content-and-date";
- DateTime leftDateTime = DateTime.Parse(leftDateTimeStr, System.Globalization.CultureInfo.InvariantCulture);
- DateTime rightDateTime = DateTime.Parse(rightDateTimeStr, System.Globalization.CultureInfo.InvariantCulture);
+ var leftDateTime = DateTime.Parse(leftDateTimeStr, CultureInfo.InvariantCulture);
+ var rightDateTime = DateTime.Parse(rightDateTimeStr, CultureInfo.InvariantCulture);
var comparisonItem = PrepareComparisonWithTwoContents(
"A1", leftHash, leftDateTime,
"B1", rightHash, rightDateTime);
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().Be(expectedResult);
}
@@ -52,18 +53,18 @@ public void Test_ContentsAndDate_Simplified(string leftHash, string leftDateTime
string @operator, bool expectedResult)
{
// Arrange
- var filterText = $"A1.contents-and-date{@operator}B1._";
+ var filterText = $"A1.content-and-date{@operator}B1._";
- DateTime leftDateTime = DateTime.Parse(leftDateTimeStr, System.Globalization.CultureInfo.InvariantCulture);
- DateTime rightDateTime = DateTime.Parse(rightDateTimeStr, System.Globalization.CultureInfo.InvariantCulture);
+ var leftDateTime = DateTime.Parse(leftDateTimeStr, CultureInfo.InvariantCulture);
+ var rightDateTime = DateTime.Parse(rightDateTimeStr, CultureInfo.InvariantCulture);
var comparisonItem = PrepareComparisonWithTwoContents(
"A1", leftHash, leftDateTime,
"B1", rightHash, rightDateTime);
-
+
// Act
var result = EvaluateFilterExpression(filterText, comparisonItem);
-
+
// Assert
result.Should().Be(expectedResult);
}
diff --git a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_DocumentationExamples.cs b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_DocumentationExamples.cs
index 7aec7a65..2770c642 100644
--- a/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_DocumentationExamples.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Business/Filtering/TestFiltering_DocumentationExamples.cs
@@ -84,7 +84,7 @@ public void TestEvaluate_LogFilesWithActions(string filterText, bool expectedRes
var comparisonItem = PrepareComparisonWithOneContent("A1", "sameHash", DateTime.Now, 1024, "app.log");
// Add copy action
- var copyAction = CreateAtomicAction(comparisonItem, ActionOperatorTypes.SynchronizeContentAndDate, false);
+ var copyAction = CreateAtomicAction(comparisonItem, ActionOperatorTypes.Copy, false);
_mockActionRepository.AddOrUpdate(new List { copyAction });
// Act
@@ -167,7 +167,7 @@ public void TestParse_FileAndDirectoryWithSize()
public void TestParse_DuplicatedFiles()
{
// Arrange
- var filterText = "A1.contents==A2.contents AND A1.size==A2.size AND is:file";
+ var filterText = "A1.content==A2.content AND A1.size==A2.size AND is:file";
ConfigureDataPartIndexer("A", "A"); // Need A1 and A2 from same inventory A
@@ -254,20 +254,20 @@ private void ConfigureDataPartIndexer(string inventoryACode = "A", string invent
var mockDataPartIndexer = Container.Resolve>();
var inventoryA = new Inventory
{
- InventoryId = $"Id_{inventoryACode}",
+ InventoryId = $"Id_{inventoryACode}",
Code = inventoryACode
};
var inventoryB = new Inventory
{
- InventoryId = $"Id_{inventoryBCode}",
+ InventoryId = $"Id_{inventoryBCode}",
Code = inventoryBCode
};
-
- var inventoryPartA = new InventoryPart(inventoryA, $"/testRoot{inventoryACode}",
+
+ var inventoryPartA = new InventoryPart(inventoryA, $"/testRoot{inventoryACode}",
FileSystemTypes.Directory);
- var inventoryPartB = new InventoryPart(inventoryB, $"/testRoot{inventoryBCode}",
+ var inventoryPartB = new InventoryPart(inventoryB, $"/testRoot{inventoryBCode}",
FileSystemTypes.Directory);
-
+
var dataPartAName = $"{inventoryACode}1";
var dataPartBName = $"{inventoryBCode}1";
var dataPartA2Name = $"{inventoryACode}2"; // For A2 if needed
@@ -275,7 +275,7 @@ private void ConfigureDataPartIndexer(string inventoryACode = "A", string invent
var dataPartA = new DataPart(dataPartAName, inventoryPartA);
var dataPartB = new DataPart(dataPartBName, inventoryPartB);
var dataPartA2 = new DataPart(dataPartA2Name, inventoryPartA);
-
+
mockDataPartIndexer.Setup(m => m.GetDataPart(dataPartAName)).Returns(dataPartA);
mockDataPartIndexer.Setup(m => m.GetDataPart(dataPartBName)).Returns(dataPartB);
mockDataPartIndexer.Setup(m => m.GetDataPart(dataPartA2Name)).Returns(dataPartA2);
@@ -285,12 +285,12 @@ private AtomicAction CreateAtomicAction(ComparisonItem comparisonItem, ActionOpe
{
var action = new AtomicAction($"AAID_{Guid.NewGuid()}", comparisonItem);
action.Operator = operatorType;
-
+
if (!isTargeted)
{
action.SynchronizationRule = new SynchronizationRule(comparisonItem.FileSystemType, ConditionModes.All);
}
-
+
return action;
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/ByteSync.Client.IntegrationTests/Services/Actions/TestSharedAtomicActionComputer.cs b/tests/ByteSync.Client.IntegrationTests/Services/Actions/TestSharedAtomicActionComputer.cs
index a2375f0d..52817ecf 100644
--- a/tests/ByteSync.Client.IntegrationTests/Services/Actions/TestSharedAtomicActionComputer.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Services/Actions/TestSharedAtomicActionComputer.cs
@@ -21,13 +21,12 @@
namespace ByteSync.Client.IntegrationTests.Services.Actions;
-
[TestFixture]
public class TestSharedAtomicActionComputer : IntegrationTest
{
private ComparisonResultPreparer _comparisonResultPreparer;
private SharedAtomicActionComputer _sharedAtomicActionComputer;
-
+
[SetUp]
public void Setup()
{
@@ -43,17 +42,17 @@ public void Setup()
contextHelper.GenerateSession();
contextHelper.GenerateCurrentEndpoint();
var testDirectory = _testDirectoryService.CreateTestDirectory();
-
+
var mockEnvironmentService = Container.Resolve>();
mockEnvironmentService.Setup(m => m.AssemblyFullName).Returns(IOUtils.Combine(testDirectory.FullName, "Assembly", "Assembly.exe"));
-
+
var mockLocalApplicationDataManager = Container.Resolve>();
- mockLocalApplicationDataManager.Setup(m => m.ApplicationDataPath).Returns(IOUtils.Combine(testDirectory.FullName,
+ mockLocalApplicationDataManager.Setup(m => m.ApplicationDataPath).Returns(IOUtils.Combine(testDirectory.FullName,
"ApplicationDataPath"));
// var atomicActionRepository = Container.Resolve>();
// atomicActionRepository.Setup(m => m.GetAtomicActions(It.IsAny())).Returns(new List());
-
+
_testDirectoryService.CreateTestDirectory();
_comparisonResultPreparer = Container.Resolve();
_sharedAtomicActionComputer = Container.Resolve();
@@ -73,14 +72,15 @@ public async Task Test_OneComparisonItem()
_testDirectoryService.CreateFileInDirectory(dataA, "file1.txt", "contentA");
var dataB = _testDirectoryService.CreateSubTestDirectory("dataB");
_testDirectoryService.CreateFileInDirectory(dataB, "file1.txt", "contentB_");
-
+
InventoryData inventoryDataA = new InventoryData(dataA);
InventoryData inventoryDataB = new InventoryData(dataB);
-
+
SessionSettings sessionSettings = SessionSettingsGenerator.GenerateSessionSettings(analysisMode: AnalysisModes.Smart);
- ComparisonResult comparisonResult = await _comparisonResultPreparer.BuildAndCompare(sessionSettings, inventoryDataA, inventoryDataB);
-
+ ComparisonResult comparisonResult =
+ await _comparisonResultPreparer.BuildAndCompare(sessionSettings, inventoryDataA, inventoryDataB);
+
// List comparisonItemViewModels = new List();
// foreach (var comparisonItem in comparisonResult.ComparisonItems)
// {
@@ -89,80 +89,81 @@ public async Task Test_OneComparisonItem()
//
// comparisonItemViewModels.Add(comparisonItemViewModel);
// }
-
+
var atomicActions = new List()
{
new()
{
AtomicActionId = $"AAID_{Guid.NewGuid()}",
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Source = inventoryDataA.GetSingleDataPart(),
Destination = inventoryDataB.GetSingleDataPart(),
ComparisonItem = comparisonResult.ComparisonItems.First()
}
};
-
+
var atomicActionRepository = Container.Resolve();
atomicActionRepository.AddOrUpdate(atomicActions);
+
// atomicActionRepository.Setup(m => m.Elements).Returns(atomicActions);
// var synchronizationActionViewModel = BuildSynchronizationAction(ActionOperatorTypes.SynchronizeContentAndDate, inventoryDataA, inventoryDataB);
// comparisonItemViewModels.First().TD_SynchronizationActions.Add(synchronizationActionViewModel);
-
+
// var synchronizationActionConverter = BuildSharedAtomicActionComputer(comparisonItemViewModels);
var sharedAtomicActions = await _sharedAtomicActionComputer.ComputeSharedAtomicActions();
-
+
ClassicAssert.AreEqual(1, sharedAtomicActions.Count);
var sharedAtomicAction = sharedAtomicActions.Single();
-
+
ClassicAssert.IsNotEmpty(inventoryDataA.AllFileDescriptions[0].SignatureGuid);
ClassicAssert.AreEqual(inventoryDataA.AllFileDescriptions[0].SignatureGuid, sharedAtomicAction.Source!.SignatureGuid);
-
+
ClassicAssert.IsNotEmpty(inventoryDataB.AllFileDescriptions[0].SignatureGuid);
ClassicAssert.AreEqual(inventoryDataB.AllFileDescriptions[0].SignatureGuid, sharedAtomicAction.Target!.SignatureGuid);
}
-
+
/*
[Test]
public async Task Test_OneComparisonItem_DoNothing()
{
CreateTestDirectory();
-
+
var dataA = _testDirectoryService.CreateSubTestDirectory("dataA");
CreateFileInDirectory(dataA, "file1.txt", "contentA");
var dataB = _testDirectoryService.CreateSubTestDirectory("dataB");
CreateFileInDirectory(dataB, "file1.txt", "contentB_");
-
+
InventoryData inventoryDataA = new InventoryData(dataA);
InventoryData inventoryDataB = new InventoryData(dataB);
-
+
SessionSettings sessionSettings = new SessionSettings();
sessionSettings.AnalysisMode = AnalysisModes.Smart;
-
+
ComparisonResultPreparer comparisonResultPreparer = new ComparisonResultPreparer(TestDirectory);
ComparisonResult comparisonResult = await comparisonResultPreparer.BuildAndCompare(sessionSettings, inventoryDataA, inventoryDataB);
-
+
List comparisonItemViewModels = new List();
foreach (var comparisonItem in comparisonResult.ComparisonItems)
{
ComparisonItemViewModel comparisonItemViewModel = new ComparisonItemViewModel(comparisonItem,
new List { inventoryDataA.Inventory, inventoryDataB.Inventory }, _mockObjectsGenerator.SessionDataHolder.Object);
-
+
comparisonItemViewModels.Add(comparisonItemViewModel);
}
-
+
var synchronizationActionViewModel = BuildSynchronizationAction(ActionOperatorTypes.SynchronizeContentAndDate, inventoryDataA, inventoryDataB);
comparisonItemViewModels.First().TD_SynchronizationActions.Add(synchronizationActionViewModel);
synchronizationActionViewModel = BuildSynchronizationAction(ActionOperatorTypes.DoNothing);
comparisonItemViewModels.First().TD_SynchronizationActions.Add(synchronizationActionViewModel);
-
+
var synchronizationActionConverter = BuildSharedAtomicActionComputer(comparisonItemViewModels);
var sharedAtomicActions = synchronizationActionConverter.ComputeSharedAtomicActions();
-
+
ClassicAssert.AreEqual(0, sharedAtomicActions.Count);
}
-
+
// private SynchronizationActionViewModel BuildSynchronizationAction(ActionOperatorTypes @operator)
// {
//
@@ -179,12 +180,12 @@ public async Task Test_OneComparisonItem_DoNothing()
//
// return synchronizationActionViewModel;
// }
-
+
private SynchronizationActionViewModel BuildSynchronizationAction(ActionOperatorTypes @operator, InventoryData source, InventoryData target)
{
return BuildSynchronizationAction(@operator, source.InventoryParts.Single(), target.InventoryParts.Single());
}
-
+
// private SynchronizationActionViewModel BuildSynchronizationAction(ActionOperatorTypes @operator, InventoryPart source, InventoryPart target)
// {
// // todo 050523
@@ -202,19 +203,19 @@ private SynchronizationActionViewModel BuildSynchronizationAction(ActionOperator
//
// return synchronizationActionViewModel;
// }
-
+
[SetUp]
public void Setup()
{
_mockObjectsGenerator = new MockObjectsGenerator(this);
_mockObjectsGenerator.GenerateCloudSessionManager();
}
-
+
private SharedAtomicActionComputer BuildSharedAtomicActionComputer(
params ComparisonItemViewModel[] comparisonItems)
{
List comparisonItemsList = new List(comparisonItems);
-
+
return BuildSharedAtomicActionComputer(comparisonItemsList);
}*/
diff --git a/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TargetInaccessible_IntegrationTests.cs b/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TargetInaccessible_IntegrationTests.cs
index 9df2c7b5..5aba9cce 100644
--- a/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TargetInaccessible_IntegrationTests.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TargetInaccessible_IntegrationTests.cs
@@ -90,7 +90,7 @@ public async Task Synchronize_Fails_When_Target_File_Inaccessible_Windows()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = invA.GetSingleDataPart(),
Destination = invB.GetSingleDataPart(),
ComparisonItem = targetItem
@@ -145,7 +145,7 @@ public async Task Synchronize_Fails_When_Target_File_Inaccessible_Posix()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = invA.GetSingleDataPart(),
Destination = invB.GetSingleDataPart(),
ComparisonItem = targetItem
diff --git a/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TestAtomicActionConsistencyChecker.cs b/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TestAtomicActionConsistencyChecker.cs
index e9f41e00..2a821e14 100644
--- a/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TestAtomicActionConsistencyChecker.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Services/Comparisons/TestAtomicActionConsistencyChecker.cs
@@ -198,7 +198,7 @@ public async Task Test_FileOnlyOnA_1()
atomicAction = new AtomicAction
{
Source = inventoryDataA.GetSingleDataPart(),
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = inventoryDataB.GetSingleDataPart()
};
@@ -212,9 +212,9 @@ public async Task Test_FileOnlyOnA_1()
}
[Test]
- [TestCase(ActionOperatorTypes.SynchronizeContentAndDate, 0)]
- [TestCase(ActionOperatorTypes.SynchronizeContentOnly, 0)]
- [TestCase(ActionOperatorTypes.SynchronizeDate, 0)]
+ [TestCase(ActionOperatorTypes.Copy, 0)]
+ [TestCase(ActionOperatorTypes.CopyContentOnly, 0)]
+ [TestCase(ActionOperatorTypes.CopyDatesOnly, 0)]
public async Task Test_FileOnAAndB_SameContentAndDate(ActionOperatorTypes actionOperator, int expectedValidItems)
{
AtomicAction atomicAction;
@@ -268,7 +268,7 @@ public void Test_SynchronizeOperationOnDirectoryNotAllowed()
var atomicAction = new AtomicAction
{
Source = new DataPart("A1", inventoryPartA),
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = new DataPart("B1", inventoryPartB)
};
@@ -326,7 +326,7 @@ public void Test_SourceRequiredForSynchronizeOperation()
var atomicAction = new AtomicAction
{
Source = null, // Missing source
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = new DataPart("B1", inventoryPartB)
};
@@ -353,7 +353,7 @@ public void Test_DestinationRequiredForSynchronizeOperation()
var atomicAction = new AtomicAction
{
Source = new DataPart("A1", inventoryPartA),
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = null // Missing destination
};
@@ -643,7 +643,7 @@ public void Test_CheckAdvancedConsistency_AllTargetsHaveAnalysisError()
var atomicAction = new AtomicAction
{
Source = new DataPart("A1", inventoryPartA),
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = new DataPart("B1", inventoryPartB)
};
@@ -850,7 +850,7 @@ public async Task Test_SourceCannotBeDestinationOfAnotherAction()
var newAction = new AtomicAction
{
Source = commonDataPart, // Same as the destination of existingAction
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = inventoryDataB.GetSingleDataPart()
};
@@ -893,7 +893,7 @@ public async Task Test_DestinationCannotBeSourceOfAnotherAction()
var existingAction = new AtomicAction
{
Source = commonDataPart, // This source will be the destination of the new action
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = inventoryDataB.GetSingleDataPart()
};
@@ -1001,7 +1001,7 @@ public async Task Test_ComplementaryActions_SynchronizeDateAndSynchronizeContent
var existingSynchronizeDateAction = new AtomicAction
{
Source = inventoryDataA.GetSingleDataPart(),
- Operator = ActionOperatorTypes.SynchronizeDate,
+ Operator = ActionOperatorTypes.CopyDatesOnly,
Destination = commonDestination
};
@@ -1009,7 +1009,7 @@ public async Task Test_ComplementaryActions_SynchronizeDateAndSynchronizeContent
var newSynchronizeContentOnlyAction = new AtomicAction
{
Source = inventoryDataA.GetSingleDataPart(), // Same source
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Destination = commonDestination // Same destination
};
@@ -1059,7 +1059,7 @@ public async Task Test_DestinationAlreadyUsedByNonComplementaryAction_MultipleAc
var existingAction2 = new AtomicAction
{
Source = inventoryDataA.GetSingleDataPart(),
- Operator = ActionOperatorTypes.SynchronizeDate,
+ Operator = ActionOperatorTypes.CopyDatesOnly,
Destination = commonDestination
};
@@ -1067,7 +1067,7 @@ public async Task Test_DestinationAlreadyUsedByNonComplementaryAction_MultipleAc
var newAction = new AtomicAction
{
Source = inventoryDataA.GetSingleDataPart(),
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Destination = commonDestination
};
@@ -1114,7 +1114,7 @@ public async Task Test_CannotDeleteItemAlreadyUsedInAnotherAction()
var existingAction = new AtomicAction
{
Source = sourceItem,
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = itemToDelete // itemToDelete is used as destination
};
@@ -1177,7 +1177,7 @@ public async Task Test_CannotOperateOnItemBeingDeleted()
var newAction = new AtomicAction
{
Source = itemBeingDeleted, // Tries to use the item being deleted as source
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Destination = otherItem
};
diff --git a/tests/ByteSync.Client.IntegrationTests/Services/Synchronizations/TestSynchronizationActionHandler.cs b/tests/ByteSync.Client.IntegrationTests/Services/Synchronizations/TestSynchronizationActionHandler.cs
index c27aa5c6..a1f4cc82 100644
--- a/tests/ByteSync.Client.IntegrationTests/Services/Synchronizations/TestSynchronizationActionHandler.cs
+++ b/tests/ByteSync.Client.IntegrationTests/Services/Synchronizations/TestSynchronizationActionHandler.cs
@@ -43,34 +43,34 @@ public void SetUp()
RegisterType();
RegisterType();
BuildMoqContainer();
-
+
var contextHelper = new TestContextGenerator(Container);
contextHelper.GenerateSession();
_currentEndPoint = contextHelper.GenerateCurrentEndpoint();
var testDirectory = _testDirectoryService.CreateTestDirectory();
-
+
// Ensure SynchronizationActionHandler sees the expected CurrentEndPoint
var mockConnectionService = Container.Resolve>();
mockConnectionService.Setup(m => m.CurrentEndPoint).Returns(_currentEndPoint);
-
+
var mockEnvironmentService = Container.Resolve>();
mockEnvironmentService.Setup(m => m.AssemblyFullName).Returns(IOUtils.Combine(testDirectory.FullName, "Assembly", "Assembly.exe"));
-
+
var mockLocalApplicationDataManager = Container.Resolve>();
- mockLocalApplicationDataManager.Setup(m => m.ApplicationDataPath).Returns(IOUtils.Combine(testDirectory.FullName,
+ mockLocalApplicationDataManager.Setup(m => m.ApplicationDataPath).Returns(IOUtils.Combine(testDirectory.FullName,
"ApplicationDataPath"));
-
+
_synchronizationActionHandler = Container.Resolve();
}
[Test]
public async Task Test_FailUnknownOperator()
- {
+ {
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_Test",
};
-
+
await FluentActions.Awaiting(() => _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup))
.Should().ThrowAsync();
}
@@ -86,16 +86,16 @@ public async Task Test_SynchronizeContentOnly_Full_LocalTarget(bool isLocalSessi
var sourceA = sourceRoot.CreateSubdirectory("A");
var sourceB = sourceRoot.CreateSubdirectory("B");
-
+
var fileADirectory = sourceA;
if (fileParentPathComplement.IsNotEmpty())
{
fileADirectory = new DirectoryInfo(sourceA.Combine(fileParentPathComplement!));
}
-
+
var fileA = _testDirectoryService.CreateFileInDirectory(fileADirectory, "fileToCopy1.txt", "fileToCopy1_content");
fileA.LastWriteTimeUtc = DateTime.UtcNow.AddHours(-8);
-
+
var fileB = new FileInfo(sourceB.Combine(fileParentPathComplement + "fileToCopy1.txt"));
fileB.Exists.Should().BeFalse();
@@ -105,7 +105,7 @@ public async Task Test_SynchronizeContentOnly_Full_LocalTarget(bool isLocalSessi
var sessionSettings = SessionSettingsGenerator.GenerateSessionSettings();
var comparisonResultPreparer = Container.Resolve();
_ = await comparisonResultPreparer.BuildAndCompare(sessionSettings, inventoryDataA, inventoryDataB);
-
+
var source = SharedDataPartTestFactory.Create("fileToCopy1.txt", inventoryDataA.Inventory, _currentEndPoint.ClientInstanceId,
FileSystemTypes.Directory, sourceA.FullName, fileParentPathComplement + "/fileToCopy1.txt", null, null, false);
@@ -115,13 +115,13 @@ public async Task Test_SynchronizeContentOnly_Full_LocalTarget(bool isLocalSessi
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_Test",
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Targets = [target],
Source = source,
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/fileToCopy1.txt", "fileToCopy1.txt", "/fileToCopy1.txt")
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
fileA.Refresh();
@@ -135,7 +135,7 @@ public async Task Test_SynchronizeContentOnly_Full_LocalTarget(bool isLocalSessi
fileB.LastWriteTimeUtc.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(30));
}
-
+
[Test]
[TestCase(true)]
[TestCase(false)]
@@ -147,29 +147,31 @@ public async Task Test_SynchronizeContentOnly_Delta_LocalTarget(bool isLocalSess
var sourceA = sourceRoot.CreateSubdirectory("A");
var sourceB = sourceRoot.CreateSubdirectory("B");
-
+
var fileADirectory = sourceA;
if (fileParentPathComplement.IsNotEmpty())
{
fileADirectory = new DirectoryInfo(sourceA.Combine(fileParentPathComplement!));
}
+
var fileA = _testDirectoryService.CreateFileInDirectory(fileADirectory, "fileToCopy1.txt", "fileToCopy1_versionA_content");
fileA.LastWriteTimeUtc = DateTime.UtcNow.AddHours(-8);
-
+
var fileBDirectory = sourceB;
if (fileParentPathComplement.IsNotEmpty())
{
fileBDirectory = new DirectoryInfo(sourceB.Combine(fileParentPathComplement!));
}
+
var fileB = _testDirectoryService.CreateFileInDirectory(fileBDirectory, "fileToCopy1.txt", "fileToCopy1_versionB_content");
-
+
var inventoryDataA = new InventoryData(sourceA);
var inventoryDataB = new InventoryData(sourceB);
var sessionSettings = SessionSettingsGenerator.GenerateSessionSettings();
var comparisonResultPreparer = Container.Resolve();
var comparisonResult = await comparisonResultPreparer.BuildAndCompare(sessionSettings, inventoryDataA, inventoryDataB);
-
+
var contentIdentityA = comparisonResult
.ComparisonItems.Single(ci => ci.FileSystemType == FileSystemTypes.File)
.ContentIdentities.Single(ci => ci.IsPresentIn(inventoryDataA.Inventory));
@@ -191,15 +193,17 @@ public async Task Test_SynchronizeContentOnly_Delta_LocalTarget(bool isLocalSess
var signatureHashB = contentIdentityB.Core!.SignatureHash;
var source = SharedDataPartTestFactory.Create("fileToCopy1.txt", inventoryDataA.Inventory, _currentEndPoint.ClientInstanceId,
- FileSystemTypes.Directory, sourceA.FullName, fileParentPathComplement + "fileToCopy1.txt", signatureGuidA, signatureHashA, false);
+ FileSystemTypes.Directory, sourceA.FullName, fileParentPathComplement + "fileToCopy1.txt", signatureGuidA, signatureHashA,
+ false);
var target = SharedDataPartTestFactory.Create("fileToCopy1.txt", inventoryDataB.Inventory, _currentEndPoint.ClientInstanceId,
- FileSystemTypes.Directory, sourceB.FullName, fileParentPathComplement + "fileToCopy1.txt", signatureGuidB, signatureHashB, false);
+ FileSystemTypes.Directory, sourceB.FullName, fileParentPathComplement + "fileToCopy1.txt", signatureGuidB, signatureHashB,
+ false);
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_Test",
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Targets = [target],
Source = source,
SynchronizationType = SynchronizationTypes.Delta,
@@ -207,7 +211,7 @@ public async Task Test_SynchronizeContentOnly_Delta_LocalTarget(bool isLocalSess
};
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
-
+
fileA.Refresh();
fileB.Refresh();
@@ -217,7 +221,7 @@ public async Task Test_SynchronizeContentOnly_Delta_LocalTarget(bool isLocalSess
var contentB = await File.ReadAllTextAsync(fileB.FullName);
contentB.Should().Be("fileToCopy1_versionA_content");
}
-
+
[Test]
[TestCase(true)]
[TestCase(false)]
@@ -227,15 +231,15 @@ public async Task Test_SynchronizeDate_LocalTarget(bool isLocalSession)
var sourceA = sourceRoot.CreateSubdirectory("A");
var sourceB = sourceRoot.CreateSubdirectory("B");
-
+
var fileA = _testDirectoryService.CreateFileInDirectory(sourceA, "fileToSync.txt", "content_A");
var originalDateA = DateTime.UtcNow.AddHours(-8);
fileA.LastWriteTimeUtc = originalDateA;
-
+
var fileB = _testDirectoryService.CreateFileInDirectory(sourceB, "fileToSync.txt", "content_B");
var originalDateB = DateTime.UtcNow.AddHours(-4);
fileB.LastWriteTimeUtc = originalDateB;
-
+
var source = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"A", sourceA.FullName, "/fileToSync.txt", null, null, false);
@@ -245,7 +249,7 @@ public async Task Test_SynchronizeDate_LocalTarget(bool isLocalSession)
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_SyncDate",
- Operator = ActionOperatorTypes.SynchronizeDate,
+ Operator = ActionOperatorTypes.CopyDatesOnly,
Targets = [target],
Source = source,
SynchronizationType = SynchronizationTypes.Full,
@@ -253,7 +257,7 @@ public async Task Test_SynchronizeDate_LocalTarget(bool isLocalSession)
CreationTimeUtc = DateTime.UtcNow.AddHours(-10),
LastWriteTimeUtc = originalDateA
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
fileA.Refresh();
@@ -269,7 +273,7 @@ public async Task Test_SynchronizeDate_LocalTarget(bool isLocalSession)
// But date should be synchronized
fileB.LastWriteTimeUtc.Should().BeCloseTo(originalDateA, TimeSpan.FromHours(4.1)); // Allow for timezone differences
}
-
+
[Test]
[TestCase(true)]
[TestCase(false)]
@@ -279,15 +283,15 @@ public async Task Test_SynchronizeContentAndDate_Full_LocalTarget(bool isLocalSe
var sourceA = sourceRoot.CreateSubdirectory("A");
var sourceB = sourceRoot.CreateSubdirectory("B");
-
+
var fileA = _testDirectoryService.CreateFileInDirectory(sourceA, "fileToSync.txt", "content_from_A");
var originalDateA = DateTime.UtcNow.AddHours(-8);
fileA.LastWriteTimeUtc = originalDateA;
-
+
var fileBPath = Path.Combine(sourceB.FullName, "fileToSync.txt");
var fileB = new FileInfo(fileBPath);
fileB.Exists.Should().BeFalse();
-
+
var source = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"A", sourceA.FullName, "/fileToSync.txt", null, null, false);
@@ -297,7 +301,7 @@ public async Task Test_SynchronizeContentAndDate_Full_LocalTarget(bool isLocalSe
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_SyncContentAndDate",
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Targets = [target],
Source = source,
SynchronizationType = SynchronizationTypes.Full,
@@ -305,7 +309,7 @@ public async Task Test_SynchronizeContentAndDate_Full_LocalTarget(bool isLocalSe
CreationTimeUtc = DateTime.UtcNow.AddHours(-10),
LastWriteTimeUtc = originalDateA
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
fileA.Refresh();
@@ -319,7 +323,7 @@ public async Task Test_SynchronizeContentAndDate_Full_LocalTarget(bool isLocalSe
contentB.Should().Be("content_from_A");
fileB.LastWriteTimeUtc.Should().BeCloseTo(originalDateA, TimeSpan.FromHours(4.1)); // Allow for timezone differences
}
-
+
[Test]
public async Task Test_Create_Directory_LocalTarget()
{
@@ -329,7 +333,7 @@ public async Task Test_Create_Directory_LocalTarget()
var targetDirectory = new DirectoryInfo(targetDirectoryPath);
targetDirectory.Exists.Should().BeFalse();
-
+
var target = SharedDataPartTestFactory.Create("NewDirectory", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/NewDirectory", null, null, false);
@@ -342,13 +346,13 @@ public async Task Test_Create_Directory_LocalTarget()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.Directory, "/NewDirectory", "NewDirectory", "/NewDirectory")
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
targetDirectory.Refresh();
targetDirectory.Exists.Should().BeTrue();
}
-
+
[Test]
public async Task Test_Create_Directory_AlreadyExists_LocalTarget()
{
@@ -357,7 +361,7 @@ public async Task Test_Create_Directory_AlreadyExists_LocalTarget()
var existingDirectory = sourceB.CreateSubdirectory("ExistingDirectory");
existingDirectory.Exists.Should().BeTrue();
-
+
var target = SharedDataPartTestFactory.Create("ExistingDirectory", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/ExistingDirectory", null, null, false);
@@ -370,14 +374,14 @@ public async Task Test_Create_Directory_AlreadyExists_LocalTarget()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.Directory, "/ExistingDirectory", "ExistingDirectory", "/ExistingDirectory")
};
-
+
// Should not throw exception even if directory already exists
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
existingDirectory.Refresh();
existingDirectory.Exists.Should().BeTrue();
}
-
+
[Test]
public async Task Test_Delete_File_LocalTarget()
{
@@ -386,7 +390,7 @@ public async Task Test_Delete_File_LocalTarget()
var fileToDelete = _testDirectoryService.CreateFileInDirectory(sourceB, "fileToDelete.txt", "content");
fileToDelete.Exists.Should().BeTrue();
-
+
var target = SharedDataPartTestFactory.Create("fileToDelete.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/fileToDelete.txt", null, null, false);
@@ -399,13 +403,13 @@ public async Task Test_Delete_File_LocalTarget()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/fileToDelete.txt", "fileToDelete.txt", "/fileToDelete.txt")
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
fileToDelete.Refresh();
fileToDelete.Exists.Should().BeFalse();
}
-
+
[Test]
public async Task Test_Delete_Directory_LocalTarget()
{
@@ -414,7 +418,7 @@ public async Task Test_Delete_Directory_LocalTarget()
var directoryToDelete = sourceB.CreateSubdirectory("DirectoryToDelete");
directoryToDelete.Exists.Should().BeTrue();
-
+
var target = SharedDataPartTestFactory.Create("DirectoryToDelete", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/DirectoryToDelete", null, null, false);
@@ -427,13 +431,13 @@ public async Task Test_Delete_Directory_LocalTarget()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.Directory, "/DirectoryToDelete", "DirectoryToDelete", "/DirectoryToDelete")
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
directoryToDelete.Refresh();
directoryToDelete.Exists.Should().BeFalse();
}
-
+
[Test]
public async Task Test_Delete_NonExistentFile_LocalTarget_ShouldNotThrow()
{
@@ -442,7 +446,7 @@ public async Task Test_Delete_NonExistentFile_LocalTarget_ShouldNotThrow()
var nonExistentFilePath = Path.Combine(sourceB.FullName, "nonExistent.txt");
File.Exists(nonExistentFilePath).Should().BeFalse();
-
+
var target = SharedDataPartTestFactory.Create("nonExistent.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/nonExistent.txt", null, null, false);
@@ -455,11 +459,11 @@ public async Task Test_Delete_NonExistentFile_LocalTarget_ShouldNotThrow()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/nonExistent.txt", "nonExistent.txt", "/nonExistent.txt")
};
-
+
// Should not throw exception for non-existent files
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
}
-
+
[Test]
public async Task Test_SynchronizeDate_FileNotExists_ShouldLogWarning()
{
@@ -468,30 +472,30 @@ public async Task Test_SynchronizeDate_FileNotExists_ShouldLogWarning()
var nonExistentFilePath = Path.Combine(sourceB.FullName, "nonExistent.txt");
File.Exists(nonExistentFilePath).Should().BeFalse();
-
+
var target = SharedDataPartTestFactory.Create("nonExistent.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/nonExistent.txt", null, null, false);
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_SyncDateNonExistent",
- Operator = ActionOperatorTypes.SynchronizeDate,
+ Operator = ActionOperatorTypes.CopyDatesOnly,
Targets = [target],
Source = null,
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/nonExistent.txt", "nonExistent.txt", "/nonExistent.txt")
};
-
+
// Should not throw but should handle the missing file gracefully
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
}
-
+
[Test]
public async Task Test_Create_InvalidFileSystemType_ShouldThrowException()
{
var sourceRoot = _testDirectoryService.CreateSubTestDirectory("Source");
var sourceB = sourceRoot.CreateSubdirectory("B");
-
+
var target = SharedDataPartTestFactory.Create("invalidFile.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/invalidFile.txt", null, null, false);
@@ -504,12 +508,12 @@ public async Task Test_Create_InvalidFileSystemType_ShouldThrowException()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/invalidFile.txt", "invalidFile.txt", "/invalidFile.txt")
};
-
+
// Should throw exception because Create operation should only work for directories
await FluentActions.Awaiting(() => _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup))
.Should().ThrowAsync();
}
-
+
[Test]
public async Task Test_Delete_Directory_WithFiles_ShouldThrowIOException()
{
@@ -522,7 +526,7 @@ public async Task Test_Delete_Directory_WithFiles_ShouldThrowIOException()
directoryToDelete.Exists.Should().BeTrue();
directoryToDelete.GetFiles().Length.Should().Be(1);
-
+
var target = SharedDataPartTestFactory.Create("DirectoryWithFiles", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/DirectoryWithFiles", null, null, false);
@@ -535,7 +539,7 @@ public async Task Test_Delete_Directory_WithFiles_ShouldThrowIOException()
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.Directory, "/DirectoryWithFiles", "DirectoryWithFiles", "/DirectoryWithFiles")
};
-
+
// Should throw IOException when trying to delete non-empty directory with recursive=false
await FluentActions.Awaiting(() => _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup))
.Should().ThrowAsync();
@@ -544,53 +548,54 @@ await FluentActions.Awaiting(() => _synchronizationActionHandler.RunSynchronizat
directoryToDelete.Refresh();
directoryToDelete.Exists.Should().BeTrue();
}
-
+
[Test]
public async Task Test_RunSynchronizationAction_CancelledImmediately_ShouldThrowOperationCancelledException()
{
var cancellationTokenSource = new CancellationTokenSource();
await cancellationTokenSource.CancelAsync();
-
+
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_Cancelled",
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Targets = [],
Source = null,
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/test.txt", "test.txt", "/test.txt")
};
-
- await FluentActions.Awaiting(() => _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup, cancellationTokenSource.Token))
+
+ await FluentActions.Awaiting(() =>
+ _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup, cancellationTokenSource.Token))
.Should().ThrowAsync();
}
-
+
[Test]
public async Task Test_SynchronizeContentOnly_RemoteTarget_ShouldCallUploader()
{
var sourceRoot = _testDirectoryService.CreateSubTestDirectory("Source");
var sourceA = sourceRoot.CreateSubdirectory("A");
var fileA = _testDirectoryService.CreateFileInDirectory(sourceA, "fileToSync.txt", "content_A");
-
+
// Create a remote target with different ClientInstanceId
var remoteClientInstanceId = Guid.NewGuid().ToString();
var source = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"A", sourceA.FullName, "/fileToSync.txt", null, null, false);
- var remoteTarget = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.File, remoteClientInstanceId,
+ var remoteTarget = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.File, remoteClientInstanceId,
"RemoteB", "C:\\RemotePath", "/fileToSync.txt", null, null, false);
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_Remote",
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Targets = [remoteTarget],
Source = source,
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/fileToSync.txt", "fileToSync.txt", "/fileToSync.txt")
};
-
+
// This should call the remote uploader (mocked in integration tests)
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
@@ -598,7 +603,7 @@ public async Task Test_SynchronizeContentOnly_RemoteTarget_ShouldCallUploader()
fileA.Refresh();
fileA.Exists.Should().BeTrue();
}
-
+
[Test]
public async Task Test_SynchronizeContentOnly_MixedLocalAndRemoteTargets()
{
@@ -610,28 +615,28 @@ public async Task Test_SynchronizeContentOnly_MixedLocalAndRemoteTargets()
var fileBPath = Path.Combine(sourceB.FullName, "fileToSync.txt");
var fileB = new FileInfo(fileBPath);
fileB.Exists.Should().BeFalse();
-
+
var remoteClientInstanceId = Guid.NewGuid().ToString();
- var source = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
+ var source = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"A", sourceA.FullName, "/fileToSync.txt", null, null, false);
var localTarget = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/fileToSync.txt", null, null, false);
-
- var remoteTarget = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, remoteClientInstanceId,
+
+ var remoteTarget = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, remoteClientInstanceId,
"RemoteC", "C:\\RemotePath", "/fileToSync.txt", null, null, false);
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_Mixed",
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Targets = [localTarget, remoteTarget],
Source = source,
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/fileToSync.txt", "fileToSync.txt", "/fileToSync.txt")
};
-
+
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup);
// Verify local target was synchronized
@@ -644,7 +649,7 @@ public async Task Test_SynchronizeContentOnly_MixedLocalAndRemoteTargets()
fileA.Refresh();
fileA.Exists.Should().BeTrue();
}
-
+
[Test]
public async Task Test_RunSynchronizationAction_MultipleTargets_CancelledDuringProcessing()
{
@@ -662,27 +667,27 @@ public async Task Test_RunSynchronizationAction_MultipleTargets_CancelledDuringP
var targetB = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"B", sourceB.FullName, "/fileToSync.txt", null, null, false);
-
+
var targetC = SharedDataPartTestFactory.Create("fileToSync.txt", FileSystemTypes.Directory, _currentEndPoint.ClientInstanceId,
"C", sourceC.FullName, "/fileToSync.txt", null, null, false);
var sharedActionsGroup = new SharedActionsGroup
{
ActionsGroupId = "ACI_MultipleTargets",
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Targets = [targetB, targetC],
Source = source,
SynchronizationType = SynchronizationTypes.Full,
PathIdentity = new PathIdentity(FileSystemTypes.File, "/fileToSync.txt", "fileToSync.txt", "/fileToSync.txt")
};
-
+
// Cancel after a short delay to potentially interrupt processing
_ = Task.Run(async () =>
{
await Task.Delay(50, CancellationToken.None);
await cancellationTokenSource.CancelAsync();
}, cancellationTokenSource.Token).ConfigureAwait(false);
-
+
try
{
await _synchronizationActionHandler.RunSynchronizationAction(sharedActionsGroup, cancellationTokenSource.Token);
@@ -696,7 +701,7 @@ public async Task Test_RunSynchronizationAction_MultipleTargets_CancelledDuringP
fileA.Refresh();
fileA.Exists.Should().BeTrue();
}
-
+
[Test]
public async Task Test_RunPendingSynchronizationActions_CloudSession_NoAbortRequest()
{
@@ -713,14 +718,14 @@ public async Task Test_RunPendingSynchronizationActions_CloudSession_NoAbortRequ
var synchronizationProcessData = new SynchronizationProcessData();
synchronizationProcessData.SynchronizationAbortRequest.OnNext(null);
mockSynchronizationService.Setup(m => m.SynchronizationProcessData).Returns(synchronizationProcessData);
-
+
// This should call Complete and HandlePendingActions
await _synchronizationActionHandler.RunPendingSynchronizationActions();
// Verify the appropriate methods were called
// Note: In integration tests, these are typically mocked services
}
-
+
[Test]
public async Task Test_RunPendingSynchronizationActions_CloudSession_WithAbortRequest()
{
@@ -738,13 +743,13 @@ public async Task Test_RunPendingSynchronizationActions_CloudSession_WithAbortRe
var abortRequest = new SynchronizationAbortRequest();
synchronizationProcessData.SynchronizationAbortRequest.OnNext(abortRequest);
mockSynchronizationService.Setup(m => m.SynchronizationProcessData).Returns(synchronizationProcessData);
-
+
// This should call Abort and ClearPendingActions
await _synchronizationActionHandler.RunPendingSynchronizationActions();
// Verify the appropriate abort methods were called
}
-
+
[Test]
public async Task Test_RunPendingSynchronizationActions_LocalSession_ShouldDoNothing()
{
@@ -753,19 +758,19 @@ public async Task Test_RunPendingSynchronizationActions_LocalSession_ShouldDoNot
var mockSessionService = Container.Resolve>();
mockSessionService.Setup(m => m.CurrentSession).Returns(localSession);
-
+
// This should do nothing for local sessions
await _synchronizationActionHandler.RunPendingSynchronizationActions();
// Since it's a local session, no remote operations should be performed
}
-
+
[Test]
public async Task Test_RunPendingSynchronizationActions_CancelledImmediately()
{
var cancellationTokenSource = new CancellationTokenSource();
await cancellationTokenSource.CancelAsync();
-
+
await FluentActions.Awaiting(() => _synchronizationActionHandler.RunPendingSynchronizationActions(cancellationTokenSource.Token))
.Should().ThrowAsync();
}
diff --git a/tests/ByteSync.Client.UnitTests/Business/Filtering/IdentifiersTest.cs b/tests/ByteSync.Client.UnitTests/Business/Filtering/IdentifiersTest.cs
index afb0a01c..a2debe15 100644
--- a/tests/ByteSync.Client.UnitTests/Business/Filtering/IdentifiersTest.cs
+++ b/tests/ByteSync.Client.UnitTests/Business/Filtering/IdentifiersTest.cs
@@ -28,7 +28,7 @@ public void All_ShouldContainAllPublicConstStrings()
public void All_ShouldContain_ACTION_SYNCHRONIZE_CONTENT()
{
// Arrange
- var expectedConstant = Identifiers.ACTION_COPY_CONTENTS;
+ var expectedConstant = Identifiers.ACTION_COPY_CONTENT;
// Act
var actualConstants = Identifiers.All();
diff --git a/tests/ByteSync.Client.UnitTests/Services/Actions/SharedActionsGroupOrganizer_UnitTests.cs b/tests/ByteSync.Client.UnitTests/Services/Actions/SharedActionsGroupOrganizer_UnitTests.cs
index 56912ad3..57085136 100644
--- a/tests/ByteSync.Client.UnitTests/Services/Actions/SharedActionsGroupOrganizer_UnitTests.cs
+++ b/tests/ByteSync.Client.UnitTests/Services/Actions/SharedActionsGroupOrganizer_UnitTests.cs
@@ -54,7 +54,7 @@ public async Task OrganizeSharedActionsGroups_WhenSynchronizeContentAndDate_Shou
{
new()
{
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
ActionsGroupId = "AGId1",
PathIdentity = new PathIdentity(FileSystemTypes.File, "file1", "file1", "file1"),
Source = SharedDataPartTestFactory.Create("A1", FileSystemTypes.Directory, "CID1", "A", "root", "relative", null, null,
@@ -69,7 +69,7 @@ public async Task OrganizeSharedActionsGroups_WhenSynchronizeContentAndDate_Shou
Size = null,
CreationTimeUtc = null,
LastWriteTimeUtc = null,
- AppliesOnlySynchronizeDate = false,
+ AppliesOnlyCopyDate = false,
}
};
@@ -99,7 +99,7 @@ public async Task OrganizeSharedActionsGroups_WhenSynchronizeContentAndDate_Shou
[Test]
[TestCase("CID1", 0)]
[TestCase("CID2", 1)]
- public async Task OrganizeSharedActionsGroups_When_SynchronizeContentAndDate_And_AppliesOnlySynchronizeDate_ShouldSortCorrectly(
+ public async Task OrganizeSharedActionsGroups_When_Copy_And_AppliesOnlyCopyDate_ShouldSortCorrectly(
string clientInstanceId, int expectedCount)
{
// Arrange
@@ -107,7 +107,7 @@ public async Task OrganizeSharedActionsGroups_When_SynchronizeContentAndDate_And
{
new()
{
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
ActionsGroupId = "AGId1",
PathIdentity = new PathIdentity(FileSystemTypes.File, "file1", "file1", "file1"),
Source = SharedDataPartTestFactory.Create("A1", FileSystemTypes.Directory, "CID1", "A", "root", "relative", null, null,
@@ -122,7 +122,7 @@ public async Task OrganizeSharedActionsGroups_When_SynchronizeContentAndDate_And
Size = null,
CreationTimeUtc = null,
LastWriteTimeUtc = null,
- AppliesOnlySynchronizeDate = true,
+ AppliesOnlyCopyDate = true,
}
};
diff --git a/tests/ByteSync.Client.UnitTests/Services/Actions/TestSharedActionsGroupComputer.cs b/tests/ByteSync.Client.UnitTests/Services/Actions/TestSharedActionsGroupComputer.cs
index 617d0b1b..ee13b893 100644
--- a/tests/ByteSync.Client.UnitTests/Services/Actions/TestSharedActionsGroupComputer.cs
+++ b/tests/ByteSync.Client.UnitTests/Services/Actions/TestSharedActionsGroupComputer.cs
@@ -50,7 +50,7 @@ public async Task Test_1Action_1Source_1Target()
SharedDataPartTestFactory.Create("A1", FileSystemTypes.Directory, "CII_A", "A", "D:\\", "file1.txt", null, null, false);
sharedAtomicAction.Target =
SharedDataPartTestFactory.Create("B1", FileSystemTypes.Directory, "CII_B", "B", "D:\\", "file1.txt", null, null, false);
- sharedAtomicAction.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction.SynchronizationType = SynchronizationTypes.Full;
sharedAtomicAction.Size = 10;
sharedAtomicAction.LastWriteTimeUtc = dateTime;
@@ -71,9 +71,9 @@ public async Task Test_1Action_1Source_1Target()
Assert.That(capturedGroup.Count, Is.EqualTo(1));
var sharedActionGroup = capturedGroup.Single();
Assert.That(sharedActionGroup.ActionsGroupId, Does.StartWith("AGID_"));
- Assert.That(sharedActionGroup.IsSynchronizeContent, Is.True);
- Assert.That(sharedActionGroup.IsSynchronizeContentAndDate, Is.True);
- Assert.That(sharedActionGroup.IsFinallySynchronizeContentAndDate, Is.True);
+ Assert.That(sharedActionGroup.IsCopyContent, Is.True);
+ Assert.That(sharedActionGroup.IsFullCopy, Is.True);
+ Assert.That(sharedActionGroup.IsFinallyCopyContentAndDate, Is.True);
Assert.That(sharedActionGroup.IsInitialOperatingOnSourceNeeded, Is.True);
Assert.That(sharedActionGroup.NeedsOnlyOperatingOnTargets, Is.False);
Assert.That(sharedActionGroup.IsFile, Is.True);
@@ -81,12 +81,12 @@ public async Task Test_1Action_1Source_1Target()
Assert.That(sharedActionGroup.IsCreate, Is.False);
Assert.That(sharedActionGroup.IsDelete, Is.False);
Assert.That(sharedActionGroup.IsDoNothing, Is.False);
- Assert.That(sharedActionGroup.IsSynchronizeContentOnly, Is.False);
- Assert.That(sharedActionGroup.IsSynchronizeDate, Is.False);
+ Assert.That(sharedActionGroup.IsCopyContentOnly, Is.False);
+ Assert.That(sharedActionGroup.IsCopyDates, Is.False);
Assert.That(sharedActionGroup.IsFinallySynchronizeDate, Is.False);
- Assert.That(sharedActionGroup.AppliesOnlySynchronizeDate, Is.False);
+ Assert.That(sharedActionGroup.AppliesOnlyCopyDate, Is.False);
Assert.That(sharedActionGroup.LinkingKeyValue, Is.EqualTo("file1.txt"));
- Assert.That(sharedActionGroup.Operator, Is.EqualTo(ActionOperatorTypes.SynchronizeContentAndDate));
+ Assert.That(sharedActionGroup.Operator, Is.EqualTo(ActionOperatorTypes.Copy));
Assert.That(sharedActionGroup.PathIdentity, Is.EqualTo(sharedAtomicAction.PathIdentity));
Assert.That(sharedActionGroup.Size, Is.EqualTo(10));
Assert.That(sharedActionGroup.LastWriteTimeUtc, Is.EqualTo(dateTime));
@@ -146,7 +146,7 @@ public async Task Test_2Actions_1Source_2Targets_1()
SharedDataPartTestFactory.Create("A1", FileSystemTypes.Directory, "CII_A", "A", "D:\\", "file1.txt", null, null, false);
sharedAtomicAction1.Target =
SharedDataPartTestFactory.Create("B1", FileSystemTypes.Directory, "CII_B", "B", "D:\\", "file1.txt", null, null, false);
- sharedAtomicAction1.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction1.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction1.SynchronizationType = SynchronizationTypes.Full;
sharedAtomicAction1.Size = 10;
sharedAtomicAction1.LastWriteTimeUtc = dateTime;
@@ -158,7 +158,7 @@ public async Task Test_2Actions_1Source_2Targets_1()
SharedDataPartTestFactory.Create("A1", FileSystemTypes.Directory, "CII_A", "A", "D:\\", "file1.txt", null, null, false);
sharedAtomicAction2.Target =
SharedDataPartTestFactory.Create("C1", FileSystemTypes.Directory, "CII_C", "C", "D:\\", "file1.txt", null, null, false);
- sharedAtomicAction2.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction2.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction2.SynchronizationType = SynchronizationTypes.Full;
sharedAtomicAction2.Size = 10;
sharedAtomicAction2.LastWriteTimeUtc = dateTime;
@@ -184,24 +184,24 @@ public async Task Test_2Actions_1Source_2Targets_1()
ClassicAssert.IsTrue(sharedActionsGroup.ActionsGroupId.StartsWith("AGID_"));
- ClassicAssert.IsFalse(sharedActionsGroup.AppliesOnlySynchronizeDate);
+ ClassicAssert.IsFalse(sharedActionsGroup.AppliesOnlyCopyDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsCreate);
ClassicAssert.IsFalse(sharedActionsGroup.IsDelete);
ClassicAssert.IsFalse(sharedActionsGroup.IsDirectory);
ClassicAssert.IsFalse(sharedActionsGroup.IsDoNothing);
ClassicAssert.IsTrue(sharedActionsGroup.IsFile);
- ClassicAssert.IsTrue(sharedActionsGroup.IsFinallySynchronizeContentAndDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFinallyCopyContentAndDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsFinallySynchronizeDate);
ClassicAssert.IsTrue(sharedActionsGroup.IsInitialOperatingOnSourceNeeded);
ClassicAssert.IsFalse(sharedActionsGroup.NeedsOnlyOperatingOnTargets);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContent);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContentAndDate);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeContentOnly);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsCopyContent);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFullCopy);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyContentOnly);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyDates);
ClassicAssert.AreEqual(dateTime, sharedActionsGroup.LastWriteTimeUtc);
ClassicAssert.AreEqual("file1.txt", sharedActionsGroup.LinkingKeyValue);
- ClassicAssert.AreEqual(ActionOperatorTypes.SynchronizeContentAndDate, sharedActionsGroup.Operator);
+ ClassicAssert.AreEqual(ActionOperatorTypes.Copy, sharedActionsGroup.Operator);
ClassicAssert.AreEqual(sharedAtomicAction1.PathIdentity, sharedActionsGroup.PathIdentity);
ClassicAssert.AreEqual(10, sharedActionsGroup.Size);
ClassicAssert.AreEqual(sharedAtomicAction1.Source, sharedActionsGroup.Source);
@@ -225,7 +225,7 @@ public async Task Test_2Actions_1Source_2Targets_2()
"SigGuidA", "SigHashA", false);
sharedAtomicAction1.Target = SharedDataPartTestFactory.Create("B1", FileSystemTypes.Directory, "CII_B", "B", "D:\\", "file1.txt",
"SigGuidB", "SigHashBC", false);
- sharedAtomicAction1.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction1.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction1.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction1.Size = 10;
sharedAtomicAction1.LastWriteTimeUtc = dateTime;
@@ -237,7 +237,7 @@ public async Task Test_2Actions_1Source_2Targets_2()
"SigGuidA", "SigHashA", false);
sharedAtomicAction2.Target = SharedDataPartTestFactory.Create("C1", FileSystemTypes.Directory, "CII_C", "C", "D:\\", "file1.txt",
"SigGuidC", "SigHashBC", false);
- sharedAtomicAction2.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction2.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction2.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction2.Size = 10;
sharedAtomicAction2.LastWriteTimeUtc = dateTime;
@@ -262,24 +262,24 @@ public async Task Test_2Actions_1Source_2Targets_2()
ClassicAssert.IsTrue(sharedActionsGroup.ActionsGroupId.StartsWith("AGID_"));
- ClassicAssert.IsFalse(sharedActionsGroup.AppliesOnlySynchronizeDate);
+ ClassicAssert.IsFalse(sharedActionsGroup.AppliesOnlyCopyDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsCreate);
ClassicAssert.IsFalse(sharedActionsGroup.IsDelete);
ClassicAssert.IsFalse(sharedActionsGroup.IsDirectory);
ClassicAssert.IsFalse(sharedActionsGroup.IsDoNothing);
ClassicAssert.IsTrue(sharedActionsGroup.IsFile);
- ClassicAssert.IsTrue(sharedActionsGroup.IsFinallySynchronizeContentAndDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFinallyCopyContentAndDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsFinallySynchronizeDate);
ClassicAssert.IsTrue(sharedActionsGroup.IsInitialOperatingOnSourceNeeded);
ClassicAssert.IsFalse(sharedActionsGroup.NeedsOnlyOperatingOnTargets);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContent);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContentAndDate);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeContentOnly);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsCopyContent);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFullCopy);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyContentOnly);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyDates);
ClassicAssert.AreEqual(dateTime, sharedActionsGroup.LastWriteTimeUtc);
ClassicAssert.AreEqual("file1.txt", sharedActionsGroup.LinkingKeyValue);
- ClassicAssert.AreEqual(ActionOperatorTypes.SynchronizeContentAndDate, sharedActionsGroup.Operator);
+ ClassicAssert.AreEqual(ActionOperatorTypes.Copy, sharedActionsGroup.Operator);
ClassicAssert.AreEqual(sharedAtomicAction1.PathIdentity, sharedActionsGroup.PathIdentity);
ClassicAssert.AreEqual(10, sharedActionsGroup.Size);
ClassicAssert.AreEqual(sharedAtomicAction1.Source, sharedActionsGroup.Source);
@@ -302,7 +302,7 @@ public async Task Test_2Actions_1Source_2Targets_3()
"SigGuidA", "SigHashA", false);
sharedAtomicAction1.Target = SharedDataPartTestFactory.Create("B1", FileSystemTypes.Directory, "CII_B", "B", "D:\\", "file1.txt",
"SigGuidB", "SigHashB", false);
- sharedAtomicAction1.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction1.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction1.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction1.Size = 10;
sharedAtomicAction1.LastWriteTimeUtc = dateTime;
@@ -314,7 +314,7 @@ public async Task Test_2Actions_1Source_2Targets_3()
"SigGuidA", "SigHashA", false);
sharedAtomicAction2.Target = SharedDataPartTestFactory.Create("C1", FileSystemTypes.Directory, "CII_C", "C", "D:\\", "file1.txt",
"SigGuidC", "SigHashC", false);
- sharedAtomicAction2.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction2.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction2.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction2.Size = 10;
sharedAtomicAction2.LastWriteTimeUtc = dateTime;
@@ -339,24 +339,24 @@ public async Task Test_2Actions_1Source_2Targets_3()
{
ClassicAssert.IsTrue(sharedActionsGroup.ActionsGroupId.StartsWith("AGID_"));
- ClassicAssert.IsFalse(sharedActionsGroup.AppliesOnlySynchronizeDate);
+ ClassicAssert.IsFalse(sharedActionsGroup.AppliesOnlyCopyDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsCreate);
ClassicAssert.IsFalse(sharedActionsGroup.IsDelete);
ClassicAssert.IsFalse(sharedActionsGroup.IsDirectory);
ClassicAssert.IsFalse(sharedActionsGroup.IsDoNothing);
ClassicAssert.IsTrue(sharedActionsGroup.IsFile);
- ClassicAssert.IsTrue(sharedActionsGroup.IsFinallySynchronizeContentAndDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFinallyCopyContentAndDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsFinallySynchronizeDate);
ClassicAssert.IsTrue(sharedActionsGroup.IsInitialOperatingOnSourceNeeded);
ClassicAssert.IsFalse(sharedActionsGroup.NeedsOnlyOperatingOnTargets);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContent);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContentAndDate);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeContentOnly);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsCopyContent);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFullCopy);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyContentOnly);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyDates);
ClassicAssert.AreEqual(dateTime, sharedActionsGroup.LastWriteTimeUtc);
ClassicAssert.AreEqual("file1.txt", sharedActionsGroup.LinkingKeyValue);
- ClassicAssert.AreEqual(ActionOperatorTypes.SynchronizeContentAndDate, sharedActionsGroup.Operator);
+ ClassicAssert.AreEqual(ActionOperatorTypes.Copy, sharedActionsGroup.Operator);
ClassicAssert.AreEqual(sharedAtomicAction1.PathIdentity, sharedActionsGroup.PathIdentity);
ClassicAssert.AreEqual(10, sharedActionsGroup.Size);
ClassicAssert.AreEqual(sharedAtomicAction1.Source, sharedActionsGroup.Source);
@@ -379,7 +379,7 @@ public async Task Test_2Actions_1Source_2Targets_4()
"SigGuidA", "SigHashABC", false);
sharedAtomicAction1.Target = SharedDataPartTestFactory.Create("B1", FileSystemTypes.Directory, "CII_B", "B", "D:\\", "file1.txt",
"SigGuidB", "SigHashABC", false);
- sharedAtomicAction1.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction1.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction1.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction1.Size = 10;
sharedAtomicAction1.LastWriteTimeUtc = dateTime;
@@ -391,7 +391,7 @@ public async Task Test_2Actions_1Source_2Targets_4()
"SigGuidA", "SigHashABC", false);
sharedAtomicAction2.Target = SharedDataPartTestFactory.Create("C1", FileSystemTypes.Directory, "CII_C", "C", "D:\\", "file1.txt",
"SigGuidC", "SigHashABC", false);
- sharedAtomicAction2.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction2.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction2.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction2.Size = 10;
sharedAtomicAction2.LastWriteTimeUtc = dateTime;
@@ -416,24 +416,24 @@ public async Task Test_2Actions_1Source_2Targets_4()
ClassicAssert.IsTrue(sharedActionsGroup.ActionsGroupId.StartsWith("AGID_"));
- ClassicAssert.IsTrue(sharedActionsGroup.AppliesOnlySynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.AppliesOnlyCopyDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsCreate);
ClassicAssert.IsFalse(sharedActionsGroup.IsDelete);
ClassicAssert.IsFalse(sharedActionsGroup.IsDirectory);
ClassicAssert.IsFalse(sharedActionsGroup.IsDoNothing);
ClassicAssert.IsTrue(sharedActionsGroup.IsFile);
- ClassicAssert.IsFalse(sharedActionsGroup.IsFinallySynchronizeContentAndDate);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsFinallyCopyContentAndDate);
ClassicAssert.IsTrue(sharedActionsGroup.IsFinallySynchronizeDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsInitialOperatingOnSourceNeeded);
ClassicAssert.IsTrue(sharedActionsGroup.NeedsOnlyOperatingOnTargets);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContent);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContentAndDate);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeContentOnly);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsCopyContent);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFullCopy);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyContentOnly);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyDates);
ClassicAssert.AreEqual(dateTime, sharedActionsGroup.LastWriteTimeUtc);
ClassicAssert.AreEqual("file1.txt", sharedActionsGroup.LinkingKeyValue);
- ClassicAssert.AreEqual(ActionOperatorTypes.SynchronizeContentAndDate, sharedActionsGroup.Operator);
+ ClassicAssert.AreEqual(ActionOperatorTypes.Copy, sharedActionsGroup.Operator);
ClassicAssert.AreEqual(sharedAtomicAction1.PathIdentity, sharedActionsGroup.PathIdentity);
ClassicAssert.AreEqual(10, sharedActionsGroup.Size);
ClassicAssert.AreEqual(sharedAtomicAction1.Source, sharedActionsGroup.Source);
@@ -455,7 +455,7 @@ public async Task Test_2Actions_1Source_2Targets_5()
sharedAtomicAction1.Source = SharedDataPartTestFactory.Create("A1", FileSystemTypes.Directory, "CII_A", "A", "D:\\", "file1.txt",
"SigGuidA", "SigHashABC", false);
sharedAtomicAction1.Target = null;
- sharedAtomicAction1.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction1.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction1.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction1.Size = 10;
sharedAtomicAction1.LastWriteTimeUtc = dateTime;
@@ -467,7 +467,7 @@ public async Task Test_2Actions_1Source_2Targets_5()
"SigGuidA", "SigHashABC", false);
sharedAtomicAction2.Target = SharedDataPartTestFactory.Create("C1", FileSystemTypes.Directory, "CII_C", "C", "D:\\", "file1.txt",
"SigGuidC", "SigHashABC", false);
- sharedAtomicAction2.Operator = ActionOperatorTypes.SynchronizeContentAndDate;
+ sharedAtomicAction2.Operator = ActionOperatorTypes.Copy;
sharedAtomicAction2.SynchronizationType = SynchronizationTypes.Delta;
sharedAtomicAction2.Size = 10;
sharedAtomicAction2.LastWriteTimeUtc = dateTime;
@@ -492,24 +492,24 @@ public async Task Test_2Actions_1Source_2Targets_5()
ClassicAssert.IsTrue(sharedActionsGroup.ActionsGroupId.StartsWith("AGID_"));
- ClassicAssert.IsTrue(sharedActionsGroup.AppliesOnlySynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.AppliesOnlyCopyDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsCreate);
ClassicAssert.IsFalse(sharedActionsGroup.IsDelete);
ClassicAssert.IsFalse(sharedActionsGroup.IsDirectory);
ClassicAssert.IsFalse(sharedActionsGroup.IsDoNothing);
ClassicAssert.IsTrue(sharedActionsGroup.IsFile);
- ClassicAssert.IsFalse(sharedActionsGroup.IsFinallySynchronizeContentAndDate);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsFinallyCopyContentAndDate);
ClassicAssert.IsTrue(sharedActionsGroup.IsFinallySynchronizeDate);
ClassicAssert.IsFalse(sharedActionsGroup.IsInitialOperatingOnSourceNeeded);
ClassicAssert.IsTrue(sharedActionsGroup.NeedsOnlyOperatingOnTargets);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContent);
- ClassicAssert.IsTrue(sharedActionsGroup.IsSynchronizeContentAndDate);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeContentOnly);
- ClassicAssert.IsFalse(sharedActionsGroup.IsSynchronizeDate);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsCopyContent);
+ ClassicAssert.IsTrue(sharedActionsGroup.IsFullCopy);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyContentOnly);
+ ClassicAssert.IsFalse(sharedActionsGroup.IsCopyDates);
ClassicAssert.AreEqual(dateTime, sharedActionsGroup.LastWriteTimeUtc);
ClassicAssert.AreEqual("file1.txt", sharedActionsGroup.LinkingKeyValue);
- ClassicAssert.AreEqual(ActionOperatorTypes.SynchronizeContentAndDate, sharedActionsGroup.Operator);
+ ClassicAssert.AreEqual(ActionOperatorTypes.Copy, sharedActionsGroup.Operator);
ClassicAssert.AreEqual(sharedAtomicAction1.PathIdentity, sharedActionsGroup.PathIdentity);
ClassicAssert.AreEqual(10, sharedActionsGroup.Size);
ClassicAssert.AreEqual(sharedAtomicAction1.Source, sharedActionsGroup.Source);
diff --git a/tests/ByteSync.Client.UnitTests/Services/Comparisons/AtomicActionConsistencyCheckerAccessTests.cs b/tests/ByteSync.Client.UnitTests/Services/Comparisons/AtomicActionConsistencyCheckerAccessTests.cs
index 6860ee42..cf3575dc 100644
--- a/tests/ByteSync.Client.UnitTests/Services/Comparisons/AtomicActionConsistencyCheckerAccessTests.cs
+++ b/tests/ByteSync.Client.UnitTests/Services/Comparisons/AtomicActionConsistencyCheckerAccessTests.cs
@@ -69,7 +69,7 @@ public void Synchronize_Fails_When_Source_Not_Accessible()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -94,7 +94,7 @@ public void Synchronize_Fails_When_Source_Part_Incomplete_In_Flat_Mode()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -144,7 +144,7 @@ public void Synchronize_WithSourceCoreNull_DoesNotThrowException()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -168,7 +168,7 @@ public void Synchronize_Fails_When_Target_Part_Incomplete_In_Flat_Mode()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -218,7 +218,7 @@ public void Synchronize_WithTargetCoreNull_DoesNotThrowException()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -267,7 +267,7 @@ public void SynchronizeContentOnly_WithBothCoresNull_DoesNotThrowException()
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -292,7 +292,7 @@ public void Synchronize_Allows_Incomplete_Parts_In_Tree_Mode_When_Items_Accessib
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentOnly,
+ Operator = ActionOperatorTypes.CopyContentOnly,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
@@ -341,7 +341,7 @@ public void Synchronize_WithInaccessibleTargetAndNullCore_DoesNotThrowException(
var action = new AtomicAction
{
- Operator = ActionOperatorTypes.SynchronizeContentAndDate,
+ Operator = ActionOperatorTypes.Copy,
Source = new DataPart("A", src),
Destination = new DataPart("B", dst),
ComparisonItem = item
diff --git a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModelTests.cs b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModelTests.cs
new file mode 100644
index 00000000..264316e6
--- /dev/null
+++ b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/AtomicActionEditViewModelTests.cs
@@ -0,0 +1,204 @@
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.Reflection;
+using ByteSync.Business.Actions.Local;
+using ByteSync.Business.Comparisons;
+using ByteSync.Common.Business.Actions;
+using ByteSync.Common.Business.EndPoints;
+using ByteSync.Common.Business.Inventories;
+using ByteSync.Common.Business.Misc;
+using ByteSync.Interfaces.Services.Sessions;
+using ByteSync.Models.Inventories;
+using ByteSync.ViewModels.Sessions.Comparisons.Actions;
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace ByteSync.Client.UnitTests.ViewModels.Sessions.Comparisons.Actions;
+
+[TestFixture]
+public class AtomicActionEditViewModelTests
+{
+ private sealed class TestDataPartIndexer : IDataPartIndexer
+ {
+ private readonly ReadOnlyCollection _dataParts;
+
+ public TestDataPartIndexer(IReadOnlyCollection dataParts)
+ {
+ _dataParts = new ReadOnlyCollection(dataParts.ToList());
+ }
+
+ public void BuildMap(List inventories)
+ {
+ }
+
+ public ReadOnlyCollection GetAllDataParts()
+ {
+ return _dataParts;
+ }
+
+ public DataPart? GetDataPart(string? dataPartName)
+ {
+ return _dataParts.FirstOrDefault(dp => dp.Name == dataPartName);
+ }
+
+ public void Remap(ICollection synchronizationRules)
+ {
+ }
+ }
+
+ private static IDataPartIndexer BuildDataPartIndexer()
+ {
+ var endpoint = new ByteSyncEndpoint
+ {
+ ClientId = "c",
+ ClientInstanceId = "ci",
+ Version = "v",
+ OSPlatform = OSPlatforms.Windows,
+ IpAddress = "127.0.0.1"
+ };
+
+ var inventoryA = new Inventory { InventoryId = "INV_A", Code = "A", Endpoint = endpoint, MachineName = "M" };
+ var inventoryB = new Inventory { InventoryId = "INV_B", Code = "B", Endpoint = endpoint, MachineName = "M" };
+ var partA = new InventoryPart(inventoryA, "c:\\a", FileSystemTypes.Directory) { Code = "A1" };
+ var partB = new InventoryPart(inventoryB, "c:\\b", FileSystemTypes.Directory) { Code = "B1" };
+
+ var dataParts = new List
+ {
+ new("A", partA),
+ new("B", partB)
+ };
+
+ return new TestDataPartIndexer(dataParts);
+ }
+
+ [Test]
+ public void Constructor_ForFile_ShouldExposeExpectedActions()
+ {
+ var viewModel = new AtomicActionEditViewModel(FileSystemTypes.File, true, null, BuildDataPartIndexer());
+
+ var actions = GetActionOperators(viewModel);
+
+ actions.Should().Contain(ActionOperatorTypes.Copy);
+ actions.Should().Contain(ActionOperatorTypes.CopyContentOnly);
+ actions.Should().Contain(ActionOperatorTypes.CopyDatesOnly);
+ actions.Should().Contain(ActionOperatorTypes.Delete);
+ actions.Should().Contain(ActionOperatorTypes.DoNothing);
+ }
+
+ [Test]
+ public void Constructor_ForDirectory_ShouldExposeExpectedActions()
+ {
+ var viewModel = new AtomicActionEditViewModel(FileSystemTypes.Directory, true, null, BuildDataPartIndexer());
+
+ var actions = GetActionOperators(viewModel);
+
+ actions.Should().Contain(ActionOperatorTypes.Create);
+ actions.Should().Contain(ActionOperatorTypes.Delete);
+ actions.Should().Contain(ActionOperatorTypes.DoNothing);
+ actions.Should().NotContain(ActionOperatorTypes.Copy);
+ }
+
+ [Test]
+ public void ExportSynchronizationAction_WhenMissingSelections_ShouldReturnNull()
+ {
+ var viewModel = new AtomicActionEditViewModel(FileSystemTypes.File, true, null, BuildDataPartIndexer());
+
+ var result = InvokeExport(viewModel);
+
+ result.Should().BeNull();
+ }
+
+ [Test]
+ public void ExportSynchronizationAction_WithValidSelections_ShouldReturnAction()
+ {
+ var viewModel = new AtomicActionEditViewModel(FileSystemTypes.File, true, null, BuildDataPartIndexer());
+
+ var action = GetActionByOperator(viewModel, ActionOperatorTypes.Copy);
+ var sources = GetInternalEnumerable(viewModel, "Sources");
+ var destinations = GetInternalEnumerable(viewModel, "Destinations");
+
+ SetInternalProperty(viewModel, "SelectedAction", action);
+ SetInternalProperty(viewModel, "SelectedSource", FirstItem(sources));
+ SetInternalProperty(viewModel, "SelectedDestination", FirstItem(destinations));
+
+ var result = InvokeExport(viewModel);
+
+ result.Should().NotBeNull();
+ result.Operator.Should().Be(ActionOperatorTypes.Copy);
+ result.Source.Should().NotBeNull();
+ result.Destination.Should().NotBeNull();
+ }
+
+ [Test]
+ public void RemoveCommand_ShouldRaiseRemoveRequested()
+ {
+ var viewModel = new AtomicActionEditViewModel(FileSystemTypes.File, true, null, BuildDataPartIndexer());
+ var raised = false;
+
+ viewModel.RemoveRequested += (_, _) => raised = true;
+
+ viewModel.RemoveCommand.Execute().Subscribe();
+
+ raised.Should().BeTrue();
+ }
+
+ private static AtomicAction? InvokeExport(AtomicActionEditViewModel viewModel)
+ {
+ var method = typeof(AtomicActionEditViewModel).GetMethod("ExportSynchronizationAction",
+ BindingFlags.Instance | BindingFlags.NonPublic);
+ return (AtomicAction?)method!.Invoke(viewModel, null);
+ }
+
+ private static IList GetActionOperators(AtomicActionEditViewModel viewModel)
+ {
+ var actions = GetInternalEnumerable(viewModel, "Actions");
+ var results = new List();
+
+ foreach (var action in actions)
+ {
+ var property = action!.GetType().GetProperty("ActionOperatorType", BindingFlags.Instance | BindingFlags.Public);
+ results.Add((ActionOperatorTypes)property!.GetValue(action)!);
+ }
+
+ return results.ToList();
+ }
+
+ private static object GetActionByOperator(AtomicActionEditViewModel viewModel, ActionOperatorTypes operatorType)
+ {
+ var actions = GetInternalEnumerable(viewModel, "Actions");
+
+ foreach (var action in actions)
+ {
+ var property = action!.GetType().GetProperty("ActionOperatorType", BindingFlags.Instance | BindingFlags.Public);
+ var value = (ActionOperatorTypes)property!.GetValue(action)!;
+ if (value == operatorType)
+ {
+ return action;
+ }
+ }
+
+ throw new InvalidOperationException("Action not found");
+ }
+
+ private static IEnumerable GetInternalEnumerable(object target, string propertyName)
+ {
+ var property = target.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic);
+ return (IEnumerable)property!.GetValue(target)!;
+ }
+
+ private static object FirstItem(IEnumerable items)
+ {
+ foreach (var item in items)
+ {
+ return item!;
+ }
+
+ throw new InvalidOperationException("Empty collection");
+ }
+
+ private static void SetInternalProperty(object target, string propertyName, object? value)
+ {
+ var property = target.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic);
+ property!.SetValue(target, value);
+ }
+}
diff --git a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModelTests.cs b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModelTests.cs
index e3c54e22..f3fe822f 100644
--- a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModelTests.cs
+++ b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/SynchronizationRuleGlobalViewModelTests.cs
@@ -1,20 +1,28 @@
+using System.Collections;
+using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reflection;
+using Autofac;
using ByteSync.Business;
using ByteSync.Business.Actions.Local;
using ByteSync.Business.Comparisons;
using ByteSync.Business.Sessions;
using ByteSync.Common.Business.Actions;
+using ByteSync.Common.Business.EndPoints;
using ByteSync.Common.Business.Inventories;
+using ByteSync.Common.Business.Misc;
using ByteSync.Interfaces.Controls.Synchronizations;
using ByteSync.Interfaces.Dialogs;
using ByteSync.Interfaces.Factories.ViewModels;
using ByteSync.Interfaces.Services.Localizations;
using ByteSync.Interfaces.Services.Sessions;
using ByteSync.Models.Comparisons.Result;
+using ByteSync.Models.Inventories;
+using ByteSync.Services;
using ByteSync.ViewModels.Sessions.Comparisons.Actions;
using FluentAssertions;
+using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
@@ -28,6 +36,7 @@ public class SynchronizationRuleGlobalViewModelTests
private Mock _localizationService = null!;
private Mock _actionEditViewModelFactory = null!;
private Mock _synchronizationRulesService = null!;
+ private Mock> _logger = null!;
private Subject _cultureSubject = null!;
@@ -39,6 +48,7 @@ public void SetUp()
_localizationService = new Mock(MockBehavior.Strict);
_actionEditViewModelFactory = new Mock(MockBehavior.Strict);
_synchronizationRulesService = new Mock(MockBehavior.Strict);
+ _logger = new Mock>();
_cultureSubject = new Subject();
@@ -51,6 +61,128 @@ private void SetupSession(DataTypes dataType)
_sessionService.SetupGet(s => s.CurrentSessionSettings).Returns(new SessionSettings { DataType = dataType });
}
+ private sealed class TestDataPartIndexer : IDataPartIndexer
+ {
+ private readonly ReadOnlyCollection _dataParts;
+
+ public TestDataPartIndexer(IReadOnlyCollection dataParts)
+ {
+ _dataParts = new ReadOnlyCollection(dataParts.ToList());
+ }
+
+ public void BuildMap(List inventories)
+ {
+ }
+
+ public ReadOnlyCollection GetAllDataParts()
+ {
+ return _dataParts;
+ }
+
+ public DataPart? GetDataPart(string? dataPartName)
+ {
+ return _dataParts.FirstOrDefault(dp => dp.Name == dataPartName);
+ }
+
+ public void Remap(ICollection synchronizationRules)
+ {
+ }
+ }
+
+ private static TestDataPartIndexer BuildDataPartIndexer()
+ {
+ var endpoint = new ByteSyncEndpoint
+ {
+ ClientId = "c",
+ ClientInstanceId = "ci",
+ Version = "v",
+ OSPlatform = OSPlatforms.Windows,
+ IpAddress = "127.0.0.1"
+ };
+
+ var inventoryA = new Inventory { InventoryId = "INV_A", Code = "A", Endpoint = endpoint, MachineName = "M" };
+ var inventoryB = new Inventory { InventoryId = "INV_B", Code = "B", Endpoint = endpoint, MachineName = "M" };
+ var partA = new InventoryPart(inventoryA, "c:\\a", FileSystemTypes.Directory) { Code = "A1" };
+ var partB = new InventoryPart(inventoryB, "c:\\b", FileSystemTypes.Directory) { Code = "B1" };
+
+ var dataParts = new List
+ {
+ new("A", partA),
+ new("B", partB)
+ };
+
+ return new TestDataPartIndexer(dataParts);
+ }
+
+ private static AtomicConditionEditViewModel BuildValidConditionViewModel(IDataPartIndexer dataPartIndexer)
+ {
+ var conditionVm = new AtomicConditionEditViewModel(FileSystemTypes.File, dataPartIndexer);
+
+ var sourceOrProperties = (IEnumerable)GetInternalProperty(conditionVm, "SourceOrProperties");
+ var comparisonOperators = (IEnumerable)GetInternalProperty(conditionVm, "ComparisonOperators");
+ var destinations = (IEnumerable)GetInternalProperty(conditionVm, "ConditionDestinations");
+
+ SetInternalProperty(conditionVm, "SelectedSourceOrProperty", FirstWhereBoolProperty(sourceOrProperties, "IsDataPart", true));
+ SetInternalProperty(conditionVm, "SelectedComparisonOperator", FirstItem(comparisonOperators));
+ SetInternalProperty(conditionVm, "SelectedDestination", FirstWhereBoolProperty(destinations, "IsVirtual", false));
+
+ return conditionVm;
+ }
+
+ private static AtomicActionEditViewModel BuildValidActionViewModel(IDataPartIndexer dataPartIndexer)
+ {
+ var actionVm = new AtomicActionEditViewModel(FileSystemTypes.File, true, null, dataPartIndexer);
+
+ var actions = (IEnumerable)GetInternalProperty(actionVm, "Actions");
+ var sources = (IEnumerable)GetInternalProperty(actionVm, "Sources");
+
+ SetInternalProperty(actionVm, "SelectedAction", FirstItem(actions));
+ SetInternalProperty(actionVm, "SelectedSource", FirstItem(sources));
+
+ var destinations = (IEnumerable)GetInternalProperty(actionVm, "Destinations");
+ SetInternalProperty(actionVm, "SelectedDestination", FirstItem(destinations));
+
+ return actionVm;
+ }
+
+ private static object GetInternalProperty(object target, string propertyName)
+ {
+ var property = target.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic);
+
+ return property!.GetValue(target)!;
+ }
+
+ private static void SetInternalProperty(object target, string propertyName, object? value)
+ {
+ var property = target.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic);
+ property!.SetValue(target, value);
+ }
+
+ private static object FirstItem(IEnumerable items)
+ {
+ foreach (var item in items)
+ {
+ return item!;
+ }
+
+ throw new InvalidOperationException("Empty collection");
+ }
+
+ private static object FirstWhereBoolProperty(IEnumerable items, string propertyName, bool expectedValue)
+ {
+ foreach (var item in items)
+ {
+ var property = item!.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+ if (property != null && property.PropertyType == typeof(bool)
+ && (bool)property.GetValue(item)! == expectedValue)
+ {
+ return item;
+ }
+ }
+
+ throw new InvalidOperationException($"No item found with {propertyName}={expectedValue}");
+ }
+
// Test doubles to surface protected helpers
private class TestAtomicActionEditViewModel : AtomicActionEditViewModel
{
@@ -95,6 +227,7 @@ public void Constructor_WithFilesDirectories_ShouldInitDefaults_AndAddOneOfEach(
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -129,6 +262,7 @@ public void Constructor_WithDirectories_ShouldSelectDirectory_AndHideTypeSelecti
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -150,6 +284,7 @@ public void ChangingConditionMode_ShouldUpdateTrailingText()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -175,6 +310,7 @@ public void AddCommands_ShouldCreateNewAtomicVMs()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -200,6 +336,7 @@ public void RemoveRequested_ShouldRemoveItems_FromCollections()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -228,6 +365,7 @@ public void Save_WithMissingFields_ShouldShowWarning_AndNotPersist()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -241,6 +379,57 @@ public void Save_WithMissingFields_ShouldShowWarning_AndNotPersist()
_dialogService.Verify(d => d.CloseFlyout(), Times.Never);
}
+ [Test]
+ public void Save_WithValidFields_ShouldPersistAndLogSuccess()
+ {
+ SetupSession(DataTypes.FilesDirectories);
+ _localizationService.Setup(l => l[It.IsAny()]).Returns("unit");
+
+ var containerBuilder = new ContainerBuilder();
+ containerBuilder.RegisterInstance(_localizationService.Object).As();
+ ContainerProvider.Container = containerBuilder.Build();
+
+ var dataPartIndexer = BuildDataPartIndexer();
+ var conditionVm = BuildValidConditionViewModel(dataPartIndexer);
+ var actionVm = BuildValidActionViewModel(dataPartIndexer);
+
+ _actionEditViewModelFactory
+ .Setup(f => f.BuildAtomicConditionEditViewModel(It.IsAny(), It.IsAny()))
+ .Returns(conditionVm);
+
+ _actionEditViewModelFactory
+ .Setup(f => f.BuildAtomicActionEditViewModel(It.IsAny(), It.IsAny(), It.IsAny(),
+ It.IsAny?>()))
+ .Returns(actionVm);
+
+ _synchronizationRulesService
+ .Setup(s => s.AddOrUpdateSynchronizationRule(It.IsAny()));
+
+ _dialogService.Setup(d => d.CloseFlyout());
+
+ var vm = new SynchronizationRuleGlobalViewModel(
+ _dialogService.Object,
+ _sessionService.Object,
+ _localizationService.Object,
+ _actionEditViewModelFactory.Object,
+ _synchronizationRulesService.Object,
+ _logger.Object,
+ null,
+ false);
+
+ vm.SaveCommand.Execute().Subscribe();
+
+ _synchronizationRulesService.Verify(s => s.AddOrUpdateSynchronizationRule(It.IsAny()), Times.Once);
+ _dialogService.Verify(d => d.CloseFlyout(), Times.Once);
+ _logger.Verify(l => l.Log(
+ LogLevel.Information,
+ It.IsAny(),
+ It.Is((state, _) => state.ToString()!.Contains("Synchronization rule saved.")),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.Once);
+ }
+
[Test]
public void SelectedFileSystemType_Change_ShouldResetCollections()
{
@@ -254,6 +443,7 @@ public void SelectedFileSystemType_Change_ShouldResetCollections()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -297,6 +487,7 @@ public void Reset_WithBaseRule_ShouldRebuildFromRule()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
baseRule,
false);
@@ -322,6 +513,7 @@ public void Cancel_ShouldCloseFlyout_AndReset()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
@@ -350,6 +542,7 @@ public void WhenActivated_ShouldSubscribeToCultureChanges()
_localizationService.Object,
_actionEditViewModelFactory.Object,
_synchronizationRulesService.Object,
+ _logger.Object,
null,
false);
diff --git a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModelTests.cs b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModelTests.cs
index 633d6e41..b74bfc27 100644
--- a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModelTests.cs
+++ b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Comparisons/Actions/TargetedActionGlobalViewModelTests.cs
@@ -1,3 +1,5 @@
+using System.Collections;
+using System.Collections.ObjectModel;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
@@ -7,15 +9,21 @@
using ByteSync.Business.Comparisons;
using ByteSync.Business.Inventories;
using ByteSync.Client.UnitTests.Helpers;
+using ByteSync.Common.Business.Actions;
+using ByteSync.Common.Business.EndPoints;
using ByteSync.Common.Business.Inventories;
+using ByteSync.Common.Business.Misc;
using ByteSync.Interfaces.Controls.Comparisons;
using ByteSync.Interfaces.Dialogs;
using ByteSync.Interfaces.Factories.ViewModels;
using ByteSync.Interfaces.Services.Localizations;
+using ByteSync.Interfaces.Services.Sessions;
using ByteSync.Models.Comparisons.Result;
+using ByteSync.Models.Inventories;
using ByteSync.TestsCommon;
using ByteSync.ViewModels.Sessions.Comparisons.Actions;
using FluentAssertions;
+using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
@@ -30,6 +38,7 @@ public class TargetedActionGlobalViewModelTests : AbstractTester
private Mock _mockAtomicActionConsistencyChecker = null!;
private Mock _mockActionEditViewModelFactory = null!;
private Mock _mockFailureReasonService = null!;
+ private Mock> _mockLogger = null!;
private Subject _cultureSubject = null!;
private List _comparisonItems = null!;
@@ -42,6 +51,7 @@ public void SetUp()
_mockAtomicActionConsistencyChecker = new Mock();
_mockActionEditViewModelFactory = new Mock();
_mockFailureReasonService = new Mock();
+ _mockLogger = new Mock>();
_cultureSubject = new Subject();
// Setup basic mocks
@@ -69,10 +79,10 @@ public void SetUp()
.Returns(mockActionEditViewModel.Object);
}
- private ComparisonItem CreateMockComparisonItem(FileSystemTypes fileSystemType)
+ private ComparisonItem CreateMockComparisonItem(FileSystemTypes fileSystemType, string linkingKey = "test-file")
{
// Create a real ComparisonItem instance since it cannot be mocked (no parameterless constructor)
- var pathIdentity = new PathIdentity(fileSystemType, "test-file", "test-file", "test-file");
+ var pathIdentity = new PathIdentity(fileSystemType, linkingKey, linkingKey, linkingKey);
var comparisonItem = new ComparisonItem(pathIdentity);
return comparisonItem;
@@ -89,6 +99,7 @@ public void Constructor_ShouldInitializeProperties()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -124,6 +135,7 @@ public void AddAction_ShouldCallActionEditViewModelFactory()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -147,6 +159,7 @@ public void OnLocaleChanged_ShouldUpdateLocalizedMessages()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -178,6 +191,7 @@ public async Task OnLocaleChanged_WithFailureSummaries_ShouldUpdateLocalizedMess
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -186,7 +200,7 @@ public async Task OnLocaleChanged_WithFailureSummaries_ShouldUpdateLocalizedMess
var failureSummary = new ValidationFailureSummary
{
- Reason = AtomicActionValidationFailureReason.InvalidSourceCount,
+ Reason = AtomicActionValidationFailureReason.SourceMissing,
Count = 2,
LocalizedMessage = "Old message",
AffectedItems = []
@@ -194,7 +208,7 @@ public async Task OnLocaleChanged_WithFailureSummaries_ShouldUpdateLocalizedMess
viewModel.FailureSummaries.Add(failureSummary);
// Set up the new localized message before triggering the culture change
- _mockFailureReasonService.Setup(x => x.GetLocalizedMessage(AtomicActionValidationFailureReason.InvalidSourceCount))
+ _mockFailureReasonService.Setup(x => x.GetLocalizedMessage(AtomicActionValidationFailureReason.SourceMissing))
.Returns("New localized message");
// Act
@@ -205,7 +219,7 @@ public async Task OnLocaleChanged_WithFailureSummaries_ShouldUpdateLocalizedMess
// Assert
viewModel.FailureSummaries[0].LocalizedMessage.Should().Be("New localized message");
- _mockFailureReasonService.Verify(x => x.GetLocalizedMessage(AtomicActionValidationFailureReason.InvalidSourceCount), Times.Once);
+ _mockFailureReasonService.Verify(x => x.GetLocalizedMessage(AtomicActionValidationFailureReason.SourceMissing), Times.Once);
}
[Test]
@@ -219,12 +233,13 @@ public void ResetWarning_ShouldClearAllWarningProperties()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
viewModel.FailureSummaries.Add(new ValidationFailureSummary
{
- Reason = AtomicActionValidationFailureReason.InvalidSourceCount,
+ Reason = AtomicActionValidationFailureReason.SourceHasMultipleIdentities,
Count = 1,
LocalizedMessage = "Test",
AffectedItems = []
@@ -255,6 +270,7 @@ public void ShowConsistencyWarning_WithValidAndInvalidItems_ShouldSetCorrectProp
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -262,14 +278,15 @@ public void ShowConsistencyWarning_WithValidAndInvalidItems_ShouldSetCorrectProp
var result = new AtomicActionConsistencyCheckCanAddResult(_comparisonItems);
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[0], true)); // Valid
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[1],
- AtomicActionValidationFailureReason.InvalidSourceCount)); // Invalid
+ AtomicActionValidationFailureReason.SourceMissing)); // Invalid
+ var atomicAction = new AtomicAction { Operator = ActionOperatorTypes.CopyContentOnly };
// Use reflection to access private method
var showConsistencyWarningMethod = typeof(TargetedActionGlobalViewModel)
.GetMethod("ShowConsistencyWarning", BindingFlags.NonPublic | BindingFlags.Instance);
// Act
- showConsistencyWarningMethod?.Invoke(viewModel, [result]);
+ showConsistencyWarningMethod?.Invoke(viewModel, [atomicAction, result]);
// Assert
viewModel.ShowSaveValidItemsCommand.Should().BeTrue();
@@ -280,7 +297,7 @@ public void ShowConsistencyWarning_WithValidAndInvalidItems_ShouldSetCorrectProp
viewModel.AreMissingFields.Should().BeFalse();
viewModel.FailureSummaries.Should().HaveCount(1);
- viewModel.FailureSummaries[0].Reason.Should().Be(AtomicActionValidationFailureReason.InvalidSourceCount);
+ viewModel.FailureSummaries[0].Reason.Should().Be(AtomicActionValidationFailureReason.SourceMissing);
viewModel.FailureSummaries[0].Count.Should().Be(1);
viewModel.FailureSummaries[0].LocalizedMessage.Should().Be("Test failure message");
viewModel.FailureSummaries[0].AffectedItems.Should().HaveCount(1);
@@ -297,22 +314,24 @@ public void ShowConsistencyWarning_WithNoValidItems_ShouldSetNoValidItemsFlag()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
// Create real instance instead of mock - all items fail validation
var result = new AtomicActionConsistencyCheckCanAddResult(_comparisonItems);
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[0],
- AtomicActionValidationFailureReason.InvalidSourceCount)); // Invalid
+ AtomicActionValidationFailureReason.SourceHasMultipleIdentities)); // Invalid
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[1],
- AtomicActionValidationFailureReason.InvalidSourceCount)); // Invalid
+ AtomicActionValidationFailureReason.SourceHasMultipleIdentities)); // Invalid
+ var atomicAction = new AtomicAction { Operator = ActionOperatorTypes.CopyContentOnly };
// Use reflection to access private method
var showConsistencyWarningMethod = typeof(TargetedActionGlobalViewModel)
.GetMethod("ShowConsistencyWarning", BindingFlags.NonPublic | BindingFlags.Instance);
// Act
- showConsistencyWarningMethod?.Invoke(viewModel, [result]);
+ showConsistencyWarningMethod?.Invoke(viewModel, [atomicAction, result]);
// Assert
viewModel.ShowSaveValidItemsCommand.Should().BeFalse();
@@ -333,17 +352,19 @@ public void ShowConsistencyWarning_WithMultipleFailureReasons_ShouldGroupByReaso
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
// Create real instance instead of mock with multiple failure reasons
var result = new AtomicActionConsistencyCheckCanAddResult(_comparisonItems);
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[0],
- AtomicActionValidationFailureReason.InvalidSourceCount)); // First InvalidSourceCount
+ AtomicActionValidationFailureReason.SourceMissing)); // First SourceMissing
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[1],
AtomicActionValidationFailureReason.CreateOperationOnFileNotAllowed)); // Different reason
result.ValidationResults.Add(new ComparisonItemValidationResult(_comparisonItems[0],
- AtomicActionValidationFailureReason.InvalidSourceCount)); // Duplicate InvalidSourceCount
+ AtomicActionValidationFailureReason.SourceMissing)); // Duplicate SourceMissing
+ var atomicAction = new AtomicAction { Operator = ActionOperatorTypes.CopyContentOnly };
_mockFailureReasonService.Setup(x => x.GetLocalizedMessage(AtomicActionValidationFailureReason.CreateOperationOnFileNotAllowed))
.Returns("Cannot create files");
@@ -353,14 +374,14 @@ public void ShowConsistencyWarning_WithMultipleFailureReasons_ShouldGroupByReaso
.GetMethod("ShowConsistencyWarning", BindingFlags.NonPublic | BindingFlags.Instance);
// Act
- showConsistencyWarningMethod?.Invoke(viewModel, [result]);
+ showConsistencyWarningMethod?.Invoke(viewModel, [atomicAction, result]);
// Assert
viewModel.FailureSummaries.Should().HaveCount(2);
// Should be ordered by count (most frequent first)
- viewModel.FailureSummaries[0].Count.Should().Be(2); // InvalidSourceCount appears twice
- viewModel.FailureSummaries[0].Reason.Should().Be(AtomicActionValidationFailureReason.InvalidSourceCount);
+ viewModel.FailureSummaries[0].Count.Should().Be(2); // SourceMissing appears twice
+ viewModel.FailureSummaries[0].Reason.Should().Be(AtomicActionValidationFailureReason.SourceMissing);
viewModel.FailureSummaries[1].Count.Should().Be(1); // CreateOperationOnFileNotAllowed appears once
viewModel.FailureSummaries[1].Reason.Should().Be(AtomicActionValidationFailureReason.CreateOperationOnFileNotAllowed);
@@ -378,12 +399,13 @@ public void ShowMissingFieldsWarning_ShouldClearFailureSummariesAndSetMissingFie
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
viewModel.FailureSummaries.Add(new ValidationFailureSummary
{
- Reason = AtomicActionValidationFailureReason.InvalidSourceCount,
+ Reason = AtomicActionValidationFailureReason.SourceMissing,
Count = 1,
LocalizedMessage = "Test",
AffectedItems = []
@@ -414,6 +436,7 @@ public void Reset_ShouldCallActionEditViewModelFactory()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -437,6 +460,7 @@ public void Cancel_ShouldCallActionEditViewModelFactory()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -460,6 +484,7 @@ public void WhenActivated_ShouldSubscribeToCultureChanges()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -481,6 +506,7 @@ public void WhenDeactivated_ShouldDisposeSubscriptions()
_mockTargetedActionsService.Object,
_mockAtomicActionConsistencyChecker.Object,
_mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
_mockFailureReasonService.Object
);
@@ -526,4 +552,155 @@ public void ValidationFailureSummary_AffectedItemsTooltip_ShouldGenerateCorrectT
// When FileName is null, it should use LinkingKeyValue
tooltip.Should().Be("file1.txt\ndirectory1");
}
+
+ [Test]
+ public void Save_WithValidAction_ShouldLogConsistencySuccess()
+ {
+ var comparisonItems = new List
+ {
+ CreateMockComparisonItem(FileSystemTypes.File, "file1"),
+ CreateMockComparisonItem(FileSystemTypes.File, "file2")
+ };
+
+ var dataPartIndexer = BuildDataPartIndexer();
+ var actionEditViewModel = new AtomicActionEditViewModel(FileSystemTypes.File, false, comparisonItems, dataPartIndexer);
+
+ _mockActionEditViewModelFactory.Setup(x => x.BuildAtomicActionEditViewModel(
+ It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()))
+ .Returns(actionEditViewModel);
+
+ var viewModel = new TargetedActionGlobalViewModel(
+ comparisonItems,
+ _mockDialogService.Object,
+ _mockLocalizationService.Object,
+ _mockTargetedActionsService.Object,
+ _mockAtomicActionConsistencyChecker.Object,
+ _mockActionEditViewModelFactory.Object,
+ _mockLogger.Object,
+ _mockFailureReasonService.Object
+ );
+
+ ConfigureValidAction(actionEditViewModel);
+
+ var result = new AtomicActionConsistencyCheckCanAddResult(comparisonItems);
+ _mockAtomicActionConsistencyChecker.Setup(x => x.CheckCanAdd(It.IsAny(), comparisonItems))
+ .Returns(result);
+
+ viewModel.SaveCommand.Execute(Unit.Default).Subscribe();
+
+ _mockLogger.Verify(
+ x => x.Log(
+ LogLevel.Information,
+ It.IsAny(),
+ It.Is