using MAX.Notification.NotificationPublishers; using MAX.Notification; var calls = new List(); var services = new TestServices(); services.Handlers = [new First((_, _) => { calls.Add(1); return Task.CompletedTask; }), new First((_, _) => { calls.Add(99); return Task.CompletedTask; }), new Second((_, _) => { calls.Add(2); return Task.CompletedTask; })]; var publisher = new NotificationPublisher(services); INotification message = new Changed(); await publisher.Publish(message); Check(calls.SequenceEqual([1, 2]), "运行时类型分发、顺序和类型去重"); calls.Clear(); await publisher.Publish((object)message); Check(calls.SequenceEqual([1, 2]) && services.Resolutions == 2, "对象分发和每次重新解析"); services.Handlers = []; await publisher.Publish(message); services.Handlers = null; await publisher.Publish(message); await Throws(() => publisher.Publish((object)null!)); await Throws(() => publisher.Publish(new object())); await Throws(() => Task.FromResult(new NotificationPublisher(services, null!))); using var cancelled = new CancellationTokenSource(); cancelled.Cancel(); // 原版只传递取消令牌;没有处理器时即使已取消也正常完成。 await publisher.Publish(message, cancelled.Token); services.Handlers = [new First((_, _) => throw new InvalidOperationException("expected")), new Second((_, _) => { calls.Add(3); return Task.CompletedTask; })]; calls.Clear(); await Throws(() => publisher.Publish(message)); Check(calls.Count == 0, "顺序模式失败后停止"); await Throws(() => new NotificationPublisher(services, new TaskWhenAllPublisher()).Publish(message)); Check(calls.Count == 0, "原版并发模式同步异常阻止后续枚举"); var gate = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var started = 0; Task Handle(Changed _, CancellationToken token) { Check(token == cancelled.Token || !token.IsCancellationRequested, "取消令牌传递"); if (++started == 2) gate.SetResult(); return gate.Task; } services.Handlers = [new First(Handle), new Second(Handle)]; await new NotificationPublisher(services, new TaskWhenAllPublisher()) .Publish(message).WaitAsync(TimeSpan.FromSeconds(5)); Check(started == 2, "并发启动和等待全部完成"); using var during = new CancellationTokenSource(); services.Handlers = [new First((_, token) => { Check(token == during.Token, "原始令牌"); during.Cancel(); return Task.CompletedTask; }), new Second((_, token) => { token.ThrowIfCancellationRequested(); return Task.CompletedTask; })]; await Throws(() => publisher.Publish(message, during.Token)); Console.WriteLine("All notification smoke tests passed."); static void Check(bool condition, string name) { if (!condition) throw new Exception($"检查失败:{name}"); } static async Task Throws(Func action) where T : Exception { try { await action(); } catch (T) { return; } throw new Exception($"预期异常:{typeof(T).Name}"); } sealed record Changed : INotification; sealed class First(Func action) : INotificationHandler { public Task Handle(Changed notification, CancellationToken cancellationToken) => action(notification, cancellationToken); } sealed class Second(Func action) : INotificationHandler { public Task Handle(Changed notification, CancellationToken cancellationToken) => action(notification, cancellationToken); } sealed class TestServices : IServiceProvider { public INotificationHandler[]? Handlers { get; set; } public int Resolutions { get; private set; } public object? GetService(Type serviceType) { if (serviceType != typeof(IEnumerable>)) throw new Exception($"错误的解析类型:{serviceType}"); Resolutions++; return Handlers; } }