mirror of
https://github.com/tiennm99/time-mocker-csharp.git
synced 2026-08-05 12:23:58 +00:00
feat: change mock logic
This commit is contained in:
@@ -14,7 +14,7 @@ namespace TimeMocker.Hook
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MockTimeInfo
|
||||
{
|
||||
public long FakeUtcTicks; // DateTime ticks (UTC)
|
||||
public long DeltaTicks; // Offset to add to DateTime.UtcNow.Ticks (can be negative)
|
||||
public int Enabled; // 1 = mock active, 0 = passthrough
|
||||
}
|
||||
|
||||
@@ -108,9 +108,12 @@ namespace TimeMocker.Hook
|
||||
private DateTime GetFakeUtc()
|
||||
{
|
||||
var info = ReadMockInfo();
|
||||
return info.Enabled == 1
|
||||
? new DateTime(info.FakeUtcTicks, DateTimeKind.Utc)
|
||||
: DateTime.UtcNow;
|
||||
if (info.Enabled == 1)
|
||||
{
|
||||
// Return real UTC time plus the stored delta offset
|
||||
return new DateTime(DateTime.UtcNow.Ticks + info.DeltaTicks, DateTimeKind.Utc);
|
||||
}
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private void InstallHooks()
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace TimeMocker.UI.Core
|
||||
try
|
||||
{
|
||||
// Write disabled state initially so hook passes through
|
||||
entry.Shm.Write(new MockTimeInfo { Enabled = 0, FakeUtcTicks = DateTime.UtcNow.Ticks });
|
||||
entry.Shm.Write(new MockTimeInfo { Enabled = 0, DeltaTicks = 0 });
|
||||
|
||||
RemoteHooking.Inject(
|
||||
process.Id,
|
||||
@@ -74,9 +74,20 @@ namespace TimeMocker.UI.Core
|
||||
{
|
||||
if (!_injected.TryGetValue(processId, out var entry)) return;
|
||||
|
||||
long deltaTicks;
|
||||
if (enabled)
|
||||
{
|
||||
// Calculate delta: (desired fake time) - (current real UTC time)
|
||||
deltaTicks = fakeUtc.Ticks - DateTime.UtcNow.Ticks;
|
||||
}
|
||||
else
|
||||
{
|
||||
deltaTicks = 0;
|
||||
}
|
||||
|
||||
entry.Shm.Write(new MockTimeInfo
|
||||
{
|
||||
FakeUtcTicks = fakeUtc.Ticks,
|
||||
DeltaTicks = deltaTicks,
|
||||
Enabled = enabled ? 1 : 0
|
||||
});
|
||||
}
|
||||
@@ -100,7 +111,7 @@ namespace TimeMocker.UI.Core
|
||||
public void Eject(int processId)
|
||||
{
|
||||
if (!_injected.TryGetValue(processId, out var entry)) return;
|
||||
entry.Shm.Write(new MockTimeInfo { Enabled = 0 }); // disable mock first
|
||||
entry.Shm.Write(new MockTimeInfo { Enabled = 0, DeltaTicks = 0 }); // disable mock first
|
||||
entry.Shm.Dispose();
|
||||
_injected.Remove(processId);
|
||||
Log($"Ejected from [{processId}] {entry.ProcessName}");
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace TimeMocker.UI.Core
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MockTimeInfo
|
||||
{
|
||||
public long FakeUtcTicks;
|
||||
public int Enabled;
|
||||
public long DeltaTicks; // Offset to add to DateTime.UtcNow.Ticks (can be negative)
|
||||
public int Enabled; // 1 = mock active, 0 = passthrough
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ namespace TimeMocker.UI.Forms
|
||||
"timemocker-config.json");
|
||||
|
||||
public bool AutoInjectEnabled { get; set; } = true;
|
||||
public bool AutoAdvanceEnabled { get; set; } = true;
|
||||
public List<PatternRuleDto> Patterns { get; set; } = new List<PatternRuleDto>();
|
||||
|
||||
public void Save()
|
||||
@@ -70,7 +69,6 @@ namespace TimeMocker.UI.Forms
|
||||
private CheckBox chkMockEnabled;
|
||||
private Button btnApply;
|
||||
private Label lblPreview;
|
||||
private CheckBox chkAutoAdvance;
|
||||
|
||||
// -- Patterns tab
|
||||
private DataGridView dgvPatterns;
|
||||
@@ -83,11 +81,6 @@ namespace TimeMocker.UI.Forms
|
||||
private RichTextBox rtbLog;
|
||||
private Button btnClearLog;
|
||||
|
||||
// Time advancing
|
||||
private Timer _advanceTimer;
|
||||
private DateTime _fakeTimeBase;
|
||||
private DateTime _advanceStartReal;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
_config = AppConfig.Load();
|
||||
@@ -112,7 +105,6 @@ namespace TimeMocker.UI.Forms
|
||||
|
||||
// Load config settings
|
||||
chkWatcherEnabled.Checked = _config.AutoInjectEnabled;
|
||||
chkAutoAdvance.Checked = _config.AutoAdvanceEnabled;
|
||||
foreach (var pattern in _config.Patterns)
|
||||
{
|
||||
_watcher.AddRule(new PatternRule
|
||||
@@ -194,21 +186,10 @@ namespace TimeMocker.UI.Forms
|
||||
ApplyTime();
|
||||
};
|
||||
|
||||
chkAutoAdvance = new CheckBox
|
||||
{
|
||||
Text = "Auto-advance time",
|
||||
ForeColor = Color.FromArgb(220, 220, 220),
|
||||
Width = 140,
|
||||
Height = 30,
|
||||
Margin = new Padding(8, 12, 4, 0),
|
||||
Checked = true
|
||||
};
|
||||
chkAutoAdvance.CheckedChanged += ToggleAutoAdvance;
|
||||
|
||||
lblPreview = new Label
|
||||
{
|
||||
AutoSize = false,
|
||||
Width = 260,
|
||||
Width = 300,
|
||||
Height = 20,
|
||||
ForeColor = Color.FromArgb(180, 180, 180),
|
||||
Font = new Font("Segoe UI", 8.5f, FontStyle.Italic),
|
||||
@@ -217,8 +198,7 @@ namespace TimeMocker.UI.Forms
|
||||
|
||||
timeFlow.Controls.AddRange(new Control[]
|
||||
{
|
||||
chkMockEnabled, dtpDate, dtpTime, btnApply, btnSetNow,
|
||||
chkAutoAdvance, lblPreview
|
||||
chkMockEnabled, dtpDate, dtpTime, btnApply, btnSetNow, lblPreview
|
||||
});
|
||||
grpTime.Controls.Add(timeFlow);
|
||||
|
||||
@@ -247,10 +227,6 @@ namespace TimeMocker.UI.Forms
|
||||
|
||||
Controls.Add(tabMain);
|
||||
Controls.Add(grpTime);
|
||||
|
||||
// advance timer
|
||||
_advanceTimer = new Timer { Interval = 1000 };
|
||||
_advanceTimer.Tick += AdvanceTick;
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
@@ -573,52 +549,28 @@ namespace TimeMocker.UI.Forms
|
||||
_watcher.FakeUtc = dt;
|
||||
_watcher.MockEnabled = chkMockEnabled.Checked;
|
||||
|
||||
if (chkAutoAdvance.Checked)
|
||||
{
|
||||
_fakeTimeBase = GetFakeTime();
|
||||
_advanceStartReal = DateTime.Now;
|
||||
}
|
||||
|
||||
UpdateTimePreview();
|
||||
}
|
||||
|
||||
private void UpdateTimePreview()
|
||||
{
|
||||
var dt = GetFakeTime();
|
||||
lblPreview.Text = chkMockEnabled.Checked
|
||||
? $"Fake: {dt:yyyy-MM-dd HH:mm:ss} (local)"
|
||||
: "Pass-through (real time)";
|
||||
}
|
||||
|
||||
private void ToggleAutoAdvance(object sender, EventArgs e)
|
||||
{
|
||||
if (chkAutoAdvance.Checked)
|
||||
if (chkMockEnabled.Checked)
|
||||
{
|
||||
_fakeTimeBase = GetFakeTime();
|
||||
_advanceStartReal = DateTime.Now;
|
||||
_advanceTimer.Start();
|
||||
// Calculate what the current offset is
|
||||
var realNow = DateTime.Now;
|
||||
var delta = dt - realNow;
|
||||
var deltaStr = delta.TotalSeconds >= 0
|
||||
? $"+{delta.TotalSeconds:F0}s"
|
||||
: $"{delta.TotalSeconds:F0}s";
|
||||
lblPreview.Text = $"Fake: {dt:yyyy-MM-dd HH:mm:ss} (local, offset {deltaStr})";
|
||||
}
|
||||
else
|
||||
{
|
||||
_advanceTimer.Stop();
|
||||
lblPreview.Text = "Pass-through (real time)";
|
||||
}
|
||||
}
|
||||
|
||||
private void AdvanceTick(object sender, EventArgs e)
|
||||
{
|
||||
if (!chkMockEnabled.Checked) return;
|
||||
var elapsed = DateTime.Now - _advanceStartReal;
|
||||
var advanced = _fakeTimeBase + elapsed;
|
||||
// Silently update dtpDate/dtpTime without triggering ValueChanged loop
|
||||
dtpDate.ValueChanged -= (s, ev) => UpdateTimePreview();
|
||||
dtpTime.ValueChanged -= (s, ev) => UpdateTimePreview();
|
||||
dtpDate.Value = advanced;
|
||||
dtpTime.Value = advanced;
|
||||
dtpDate.ValueChanged += (s, ev) => UpdateTimePreview();
|
||||
dtpTime.ValueChanged += (s, ev) => UpdateTimePreview();
|
||||
ApplyTime();
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Helpers
|
||||
// =====================================================================
|
||||
@@ -738,7 +690,6 @@ namespace TimeMocker.UI.Forms
|
||||
{
|
||||
// Save config
|
||||
_config.AutoInjectEnabled = chkWatcherEnabled.Checked;
|
||||
_config.AutoAdvanceEnabled = chkAutoAdvance.Checked;
|
||||
_config.Patterns.Clear();
|
||||
foreach (DataGridViewRow row in dgvPatterns.Rows)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user