Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from julien-wff/feat-jobs-base
Browse files Browse the repository at this point in the history
feat: jobs base
  • Loading branch information
julien-wff authored Dec 3, 2023
2 parents b67bbed + 6652002 commit e72eae2
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 0 deletions.
10 changes: 10 additions & 0 deletions EasyLib/Enums/JobState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace EasyLib.Enums;

public enum JobState
{
End,
SourceScan,
DifferenceCalculation,
Copy,
Paused
}
7 changes: 7 additions & 0 deletions EasyLib/Enums/JobType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace EasyLib.Enums;

public enum JobType
{
Full,
Differential,
}
16 changes: 16 additions & 0 deletions EasyLib/Events/IJobStatusPublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace EasyLib.Events;

public interface IJobStatusPublisher
{
/// <summary>
/// Subscribe to the job-related events
/// </summary>
/// <param name="subscriber">Class that will be notified of the events</param>
public void Subscribe(IJobStatusSubscriber subscriber);

/// <summary>
/// Unsubscribe from the job-related events
/// </summary>
/// <param name="subscriber">Class that will stop to be notified</param>
public void Unsubscribe(IJobStatusSubscriber subscriber);
}
34 changes: 34 additions & 0 deletions EasyLib/Events/IJobStatusSubscriber.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using EasyLib.Enums;

namespace EasyLib.Events;

public interface IJobStatusSubscriber
{
/// <summary>
/// Event triggered when the job state changes (running, paused, steps...)
/// </summary>
/// <param name="state">New step of the job</param>
/// <param name="job">Concerned job</param>
public void OnJobStateChange(JobState state, Job.Job job)
{
// Implementation optional
}

/// <summary>
/// Event triggered when an error occurs during the job
/// </summary>
/// <param name="error"></param>
public void OnJobError(string error)
{
// Implementation optional
}

/// <summary>
/// Event triggered when the job progress changes (number of files, bytes, etc.)
/// </summary>
/// <param name="job">Concerned job</param>
public void OnJobProgress(Job.Job job)
{
// Implementation optional
}
}
31 changes: 31 additions & 0 deletions EasyLib/Files/StateManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace EasyLib.Files;

/// <summary>
/// Singleton to get and write the jobs to the state.json file.
/// </summary>
public class StateManager
{
public readonly string StateFilePath;

private StateManager()
{
// AppData dir and append easysave/state.json
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var stateDirectory = Path.Combine(appDataDir, "easysave");
StateFilePath = Path.Combine(stateDirectory, "state.json");

// Create directory if it doesn't exist
if (!Directory.Exists(stateDirectory))
{
Directory.CreateDirectory(stateDirectory);
}

// Create file and write [] if it doesn't exist
if (!File.Exists(StateFilePath))
{
File.WriteAllText(StateFilePath, "[]");
}
}

public static StateManager Instance { get; } = new();
}
74 changes: 74 additions & 0 deletions EasyLib/Job/Job.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using EasyLib.Enums;
using EasyLib.Events;

namespace EasyLib.Job;

/// <summary>
/// A job is a backup task that can be run by the application. It can be a copy, a move, a delete, etc.
/// </summary>
/// <param name="name">Name of the task</param>
/// <param name="sourceFolder">Folder to backup</param>
/// <param name="destinationFolder">Backup destination</param>
/// <param name="type">Backup type (full, differential)</param>
/// <param name="state">Current state</param>
public class Job(string name, string sourceFolder, string destinationFolder, JobType type,
JobState state = JobState.End) : IJobStatusPublisher
{
private readonly List<IJobStatusSubscriber> _observers = new();
public string DestinationFolder = destinationFolder;
public ulong FilesBytesCopied = 0;
public uint FilesCopied = 0;
public uint FilesCount = 0;
public ulong FilesSizeBytes = 0;
public uint Id;
public string Name = name;
public string SourceFolder = sourceFolder;
public JobState State = state;
public JobType Type = type;

public void Subscribe(IJobStatusSubscriber subscriber)
{
_observers.Add(subscriber);
}

public void Unsubscribe(IJobStatusSubscriber subscriber)
{
_observers.Remove(subscriber);
}

/// <summary>
/// Check if the job is valid (name, source and destination reachable, etc.)
/// </summary>
/// <returns>True if the job is valid</returns>
public bool Check()
{
return true;
}

/// <summary>
/// Run the backup job
/// </summary>
/// <returns>True when the job is complete</returns>
public bool Run()
{
return true;
}

/// <summary>
/// Pause a job execution
/// </summary>
/// <returns>True when the job is paused</returns>
public bool Pause()
{
return true;
}

/// <summary>
/// Cancel a running or a paused job, make its status to "End"
/// </summary>
/// <returns></returns>
public bool Cancel()
{
return true;
}
}
123 changes: 123 additions & 0 deletions EasyLib/JobManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using EasyLib.Enums;
using EasyLib.Events;

namespace EasyLib;

/// <summary>
/// Facade to all the job-related work
/// </summary>
public class JobManager : IJobStatusSubscriber, IJobStatusPublisher
{
/// <summary>
/// List of all available jobs
/// </summary>
private readonly List<Job.Job> _jobs = new();

/// <summary>
/// List of all the subscribers to the job-related events
/// </summary>
private readonly List<IJobStatusSubscriber> _subscribers = new();

public void Subscribe(IJobStatusSubscriber subscriber)
{
_subscribers.Add(subscriber);
}

public void Unsubscribe(IJobStatusSubscriber subscriber)
{
_subscribers.Remove(subscriber);
}

/// <summary>
/// Get all the jobs
/// </summary>
/// <returns>Stored jobs</returns>
public List<Job.Job> GetJobs()
{
return _jobs;
}

/// <summary>
/// Populate the list of jobs from the state.json file
/// </summary>
/// <returns>True if the operation is successful</returns>
public bool FetchJobs()
{
throw new NotImplementedException();
}

/// <summary>
/// Parse a job string into a list of jobs. IDs and names are accepted.
/// </summary>
/// <param name="jobsIString">Job string, for instance: 1-3,5,job2</param>
/// <returns>The list of found jobs</returns>
public List<Job.Job> GetJobsFromString(string jobsIString)
{
throw new NotImplementedException();
}

/// <summary>
/// Get the jobs with the specified IDs
/// </summary>
/// <param name="jobIds">List of job IDs</param>
/// <returns></returns>
public List<Job.Job> GetJobsFromIds(IEnumerable<int> jobIds)
{
var ids = jobIds.ToList().Select(id => (uint)id);
return _jobs.Where(job => ids.Contains(job.Id)).ToList();
}

/// <summary>
/// Start the execution of the specified jobs. If the jobs are paused, they are resumed.
/// </summary>
/// <param name="jobIds">IDs of the jobs to start</param>
/// <returns>True if the execution is complete</returns>
public bool ExecuteJobs(IEnumerable<int> jobIds)
{
throw new NotImplementedException();
}

/// <summary>
/// Start the execution of the specified jobs. If the jobs are paused, they are resumed.
/// </summary>
/// <param name="jobs">List of obs to start</param>
/// <returns></returns>
public bool ExecuteJobs(IEnumerable<Job.Job> jobs)
{
throw new NotImplementedException();
}


/// <summary>
/// Create a new job
/// </summary>
/// <param name="name">Job name</param>
/// <param name="src">Backup source UNC path</param>
/// <param name="dest">Backup destination UNC path</param>
/// <param name="type">Backup type</param>
/// <returns>Newly created job</returns>
public Job.Job CreateJob(string name, string src, string dest, JobType type)
{
var newJob = new Job.Job(name, src, dest, type);
_jobs.Add(newJob);
return newJob;
}

/// <summary>
/// Delete a job
/// </summary>
/// <param name="job">Job to delete</param>
public void DeleteJob(Job.Job job)
{
_jobs.Remove(job);
}

/// <summary>
/// Cancel a paused job, make its status to "End"
/// </summary>
/// <param name="job">Job to cancel</param>
public void CancelJob(Job.Job job)
{
job.Cancel();
}
}
67 changes: 67 additions & 0 deletions EasySave.Tests/EasyLib/Files/StateManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using EasyLib.Files;

namespace EasySave.Tests.EasyLib.Files;

public class StateManagerTests
{
private string GetStateFilePath()
{
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var stateDirectory = Path.Combine(appDataDir, "easysave");
return Path.Combine(stateDirectory, "state.json");
}

[Fact]
public void Instance_ShouldBeSingleton()
{
// Arrange

// Act
var instance1 = StateManager.Instance;
var instance2 = StateManager.Instance;

// Assert
Assert.Same(instance1, instance2);
}

[Fact]
public void StateFileCreation_ShouldCreateEmptyFile()
{
// Arrange

// Act
var stateManager = StateManager.Instance;
var stateFilePath = stateManager.StateFilePath;

// Assert
Assert.True(File.Exists(stateFilePath));
}

[Fact]
public void StateFileCreation_ShouldCreateJsonArray()
{
// Arrange

// Act
var stateManager = StateManager.Instance;
var stateFilePath = stateManager.StateFilePath;
var fileContent = File.ReadAllText(stateFilePath).Trim();

// Assert
Assert.StartsWith("[", fileContent);
Assert.EndsWith("]", fileContent);
}

[Fact]
public void StateFilePath_ShouldBeCorrect()
{
// Arrange

// Act
var stateManager = StateManager.Instance;
var stateFilePath = stateManager.StateFilePath;

// Assert
Assert.Equal(GetStateFilePath(), stateFilePath);
}
}
Loading

0 comments on commit e72eae2

Please sign in to comment.