v9.10.2019.0101  全面巩固批量Insert/Update/Upsert,支持数据备份、恢复和同步,支持实体列表保存到文件以及加载
大石头 authored at 2019-01-01 13:38:33
5.32 KiB
X
using System;
using System.Threading;
using System.Threading.Tasks;
using NewLife;
using NewLife.Log;
using NewLife.Security;
using NewLife.Threading;
using Xunit;

namespace XUnitTest.Threading;

public class TimerXTests
{
    static TimerXTests()
    {
        TimerScheduler.Default.Log = XTrace.Log;
    }

    [Fact]
    public void NormalTest()
    {
        var now = DateTime.Now;
        var count = 0;
        using var timer = new TimerX(s =>
        {
            Interlocked.Increment(ref count);

            //Assert.Equal(s, TimerX.Current);
        }, "NewLife", 1000, 3000, "Test");

        Assert.True(timer.Id > 0);
        Assert.Equal("Test", timer.Scheduler.Name);
        //Assert.NotNull(timer.Callback);
        Assert.Equal("NewLife", timer.State);
        Assert.True(timer.NextTick > Runtime.TickCount64);
        Assert.True(timer.NextTick <= Runtime.TickCount64 + 1000);
        Assert.True(timer.NextTime > now.AddMilliseconds(100));
        //Assert.True(timer.NextTime < now.AddMilliseconds(20));
        Assert.Equal(0, timer.Timers);
        Assert.Equal(3000, timer.Period);
        Assert.False(timer.Async);
        Assert.False(timer.Absolutely);

        //Assert.Equal(10, count);
        //Assert.Equal(10, timer.Timers);
    }

    [Fact]
    public void SyncTest()
    {
        XTrace.WriteLine("SyncTest");

        // 以构造前后的系统节拍为窗口校验首次触发时刻:NextTick = 构造时读数 + 100,
        // 这样既不受 TickCount64 在 Windows 上 15.6ms 量化影响,也不受线程被抢占导致的读数延迟影响
        var before = Runtime.TickCount64;
        using var timer = new TimerX(DoSyncTest, "SyncStone", 100, 200);
        var after = Runtime.TickCount64;

        Assert.InRange(timer.NextTick, before + 100, after + 100);

        // 留一点时间让定时器至少触发一次
        Thread.Sleep(300);
    }

    private static void DoSyncTest(Object state)
    {
        var key = Rand.NextString(8);
        XTrace.WriteLine("Begin {0} {1}", state, key);

        Thread.Sleep(100);

        XTrace.WriteLine("End {0} {1}", state, key);
    }

    [Fact]
    public void AsyncTest()
    {
        XTrace.WriteLine("AsyncTest");

        // 同 SyncTest,用构造前后的节拍窗口消除 TickCount64 量化与线程调度带来的测量误差
        var before = Runtime.TickCount64;
        using var timer = new TimerX(DoAsyncTest, "AsyncStone", 100, 200) { Async = true };
        var after = Runtime.TickCount64;

        Assert.InRange(timer.NextTick, before + 100, after + 100);

        // 留一点时间让定时器至少触发一次
        Thread.Sleep(300);
    }

    private static async Task DoAsyncTest(Object state)
    {
        var key = Rand.NextString(8);
        XTrace.WriteLine("Begin {0} {1}", state, key);

        await Task.Delay(100);

        XTrace.WriteLine("End {0} {1}", state, key);
    }

    [Fact]
    public void AbsolutelyTest()
    {
        XTrace.WriteLine("AbsolutelyTest");

        var before = Runtime.TickCount64;
        using var timer = new TimerX(DoAbsolutelyTest, "Stone2", DateTime.Today, 100);
        var after = Runtime.TickCount64;

        // 绝对定时器把 00:00 起的 100ms 网格推后到当前时刻之后,首次触发落在 (now, now+100] 区间内
        Assert.InRange(timer.NextTick, before + 1, after + 100);

        // 留一点时间让定时器至少触发一次
        Thread.Sleep(300);
    }

    private static async Task DoAbsolutelyTest(Object state)
    {
        var key = Rand.NextString(8);
        XTrace.WriteLine("Begin {0} {1}", state, key);

        await Task.Delay(110);

        XTrace.WriteLine("End {0} {1}", state, key);
    }

    [Fact]
    public void AbsolutelyBoundaryTest()
    {
        XTrace.WriteLine("AbsolutelyBoundaryTest");

        // 构造时刻距绝对网格点不足 1ms 时,间隔截断为 0 会提前触发。
        // 显式把网格点摆在当前时刻之后 0.3ms,覆盖该窗口,验证首个触发严格晚于当前时刻。
        // 奇偶轮换同步与异步构造,两条路径都覆盖
        for (var i = 0; i < 100; i++)
        {
            var start = DateTime.Now.AddMilliseconds(-99.7);
            var before = Runtime.TickCount64;
            using var timer = i % 2 == 0
                ? new TimerX(_ => { }, "Stone3", start, 100)
                : new TimerX(DoAbsolutelyTest, "Stone2", start, 100);
            var after = Runtime.TickCount64;

            Assert.InRange(timer.NextTick, before + 1, after + 100);
        }
    }

    [Fact]
    public void CronTest()
    {
        XTrace.WriteLine("CronTest");

        var before = Runtime.TickCount64;
        using var timer = new TimerX(DoCronTest, "CronTest", "1/4 * * * *;3/20");
        var after = Runtime.TickCount64;

        Assert.NotNull(timer.Crons);
        Assert.Equal(2, timer.Crons.Length);

        // 首次触发不晚于 4 秒(1/4 表示每分钟内从第 1 秒起每 4 秒一次),
        // 下界用构造时读数、上界留 1 秒余量以容纳秒级对齐
        Assert.InRange(timer.NextTick, before + 1, after + 3999 + 1000);
    }

    private static async Task DoCronTest(Object state)
    {
        var key = Rand.NextString(8);
        XTrace.WriteLine("Begin {0} {1}", state, key);

        await Task.Delay(110);

        XTrace.WriteLine("End {0} {1}", state, key);
    }
}