From fe20d33d7fda6d132fdf88f15af2473436928450 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Leite de Andrade Date: Sun, 2 Mar 2025 16:43:56 -0300 Subject: [PATCH 1/6] Trying to close pending Completers. #18 --- CHANGELOG.md | 7 +- example/example.dart | 4 +- lib/simdart.dart | 2 +- lib/src/event_phase.dart | 2 + lib/src/internal/completer_action.dart | 3 - lib/src/internal/completer_interrupt.dart | 6 + lib/src/internal/debug_listener.dart | 18 +++ lib/src/internal/event_action.dart | 79 ++++++---- lib/src/internal/repeat_event_action.dart | 3 - lib/src/internal/stop_action.dart | 19 +++ lib/src/internal/time_action.dart | 2 - lib/src/resources.dart | 16 +- lib/src/sim_context.dart | 2 + .../{sim_observer.dart => sim_listener.dart} | 19 ++- lib/src/simdart.dart | 111 ++++++++++---- test/process_test.dart | 27 ++-- test/repeat_process_test.dart | 12 +- test/resource_test.dart | 15 +- test/stop_test.dart | 140 ++++++++++++++++++ test/test_helper.dart | 53 ++++++- test/time_loop_test.dart | 3 - test/wait_test.dart | 23 +-- 22 files changed, 418 insertions(+), 148 deletions(-) create mode 100644 lib/src/internal/completer_interrupt.dart create mode 100644 lib/src/internal/debug_listener.dart create mode 100644 lib/src/internal/stop_action.dart rename lib/src/{sim_observer.dart => sim_listener.dart} (80%) create mode 100644 test/stop_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index cfa4a10..cded398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ +## 0.4.0 + +* Added + * `stop` method in `SimContext`. + ## 0.3.0 * Access the list of created resources. * Added * `runState` getter to determine whether the simulation has not started, is running, or has completed. * `stop` method to manually stop the simulation before it completes. - * Observer to receive data throughout the simulation. + * Listener to receive data throughout the simulation. ## 0.2.0 diff --git a/example/example.dart b/example/example.dart index f6cb715..79d5c73 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,7 +1,9 @@ +import 'dart:async'; + import 'package:simdart/simdart.dart'; void main() async { - final SimDart sim = SimDart(observer: ConsoleEventObserver()); + final SimDart sim = SimDart(listener: ConsoleEventListener()); sim.process(event: _eventA, name: 'A'); diff --git a/lib/simdart.dart b/lib/simdart.dart index df4118c..13f47e5 100644 --- a/lib/simdart.dart +++ b/lib/simdart.dart @@ -1,7 +1,7 @@ export 'src/event.dart'; export 'src/interval.dart'; export 'src/observable.dart'; -export 'src/sim_observer.dart'; +export 'src/sim_listener.dart'; export 'src/event_phase.dart'; export 'src/resources.dart' hide ResourcesFactory, ResourceStore; export 'src/simdart.dart' hide SimDartHelper; diff --git a/lib/src/event_phase.dart b/lib/src/event_phase.dart index 0120b5f..133c1c5 100644 --- a/lib/src/event_phase.dart +++ b/lib/src/event_phase.dart @@ -11,6 +11,8 @@ enum EventPhase { yielded, + interrupted, + finished; /// Returns the string representation of the status. diff --git a/lib/src/internal/completer_action.dart b/lib/src/internal/completer_action.dart index 0446da3..2656717 100644 --- a/lib/src/internal/completer_action.dart +++ b/lib/src/internal/completer_action.dart @@ -12,7 +12,4 @@ class CompleterAction extends TimeAction { void execute() { complete.call(); } - - @override - void dispose() {} } diff --git a/lib/src/internal/completer_interrupt.dart b/lib/src/internal/completer_interrupt.dart new file mode 100644 index 0000000..31a533f --- /dev/null +++ b/lib/src/internal/completer_interrupt.dart @@ -0,0 +1,6 @@ +import 'package:meta/meta.dart'; + +@internal +class CompleterInterrupt{ + +} \ No newline at end of file diff --git a/lib/src/internal/debug_listener.dart b/lib/src/internal/debug_listener.dart new file mode 100644 index 0000000..8b2ad12 --- /dev/null +++ b/lib/src/internal/debug_listener.dart @@ -0,0 +1,18 @@ +import 'package:meta/meta.dart'; + +@internal +abstract class DebugListener { + + void onScheduleNextAction(); + + void onNextAction(); + + void onExecuteAction(); + + void onStop(); + + void onAddCompleter(); + + void onRemoveCompleter(); +} + diff --git a/lib/src/internal/event_action.dart b/lib/src/internal/event_action.dart index b284f63..a6292de 100644 --- a/lib/src/internal/event_action.dart +++ b/lib/src/internal/event_action.dart @@ -4,6 +4,8 @@ import 'package:meta/meta.dart'; import 'package:simdart/src/event.dart'; import 'package:simdart/src/event_phase.dart'; import 'package:simdart/src/internal/completer_action.dart'; +import 'package:simdart/src/internal/completer_interrupt.dart'; +import 'package:simdart/src/internal/stop_action.dart'; import 'package:simdart/src/internal/time_action.dart'; import 'package:simdart/src/interval.dart'; import 'package:simdart/src/resources.dart'; @@ -48,50 +50,60 @@ class EventAction extends TimeAction implements SimContext { @override void execute() { + print('execute: $eventName'); if (_eventCompleter != null) { throw StateError('This event is yielding'); } - sim.observer?.onEvent( + sim.listener?.onEvent( name: eventName, time: sim.now, phase: EventPhase.called, executionHash: hashCode); - _runEvent().then((_) { - if (_eventCompleter != null) { - SimDartHelper.error( - sim: sim, - msg: - "Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?"); - return; + event(this).then((_) { + print('then $eventName ${sim.runState.name}'); + if (_eventCompleter != null && sim.runState==RunState.running) { + //TODO method or throw? + SimDartHelper.error( + sim: sim, + error: + StateError( + "Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?")); + return; + } + sim.listener?.onEvent( + name: eventName, + time: sim.now, + phase: EventPhase.finished, + executionHash: hashCode); + SimDartHelper.scheduleNextAction(sim: sim); + }).catchError((error) { + //TODO rever + if(error is CompleterInterrupt) { + sim.listener?.onEvent( + name: eventName, + time: sim.now, + phase: EventPhase.interrupted, + executionHash: hashCode); + } else { + print('ZZ'); + SimDartHelper.error(sim: sim, error: error); } - sim.observer?.onEvent( - name: eventName, - time: sim.now, - phase: EventPhase.finished, - executionHash: hashCode); - - SimDartHelper.scheduleNextAction(sim: sim); - }).catchError((e) { - // Sim already marked to finish. Let the last event finalize. }); } - Future _runEvent() async { - await event(this); - } - @override Future wait(int delay) async { if (_eventCompleter != null) { + //TODO method or throw? SimDartHelper.error( sim: sim, - msg: "The event is already waiting. Did you forget to use 'await'?"); + error: StateError("The event is already waiting. Did you forget to use 'await'?")); return; } - sim.observer?.onEvent( + sim.listener?.onEvent( name: eventName, time: sim.now, phase: EventPhase.yielded, @@ -106,9 +118,9 @@ class EventAction extends TimeAction implements SimContext { start: sim.now + delay, complete: _eventCompleter!.complete, order: order)); - SimDartHelper.scheduleNextAction(sim: sim); - await _eventCompleter!.future; + SimDartHelper.scheduleNextAction(sim: sim); + await _eventCompleter!.future; } @override @@ -133,6 +145,11 @@ class EventAction extends TimeAction implements SimContext { name: name); } + @override + void stop(){ + SimDartHelper.addAction(sim: sim, action: StopAction(start: sim.now, sim:sim)); + } + @override SimCounter counter(String name) { return sim.counter(name); @@ -142,15 +159,12 @@ class EventAction extends TimeAction implements SimContext { SimNum num(String name) { return sim.num(name); } - - @override - void dispose() { - _eventCompleter?.complete(); - } } class EventCompleter { - EventCompleter({required this.event}); + EventCompleter({required this.event}){ + SimDartHelper.addCompleter(sim: event.sim, completer: _completer); + } final Completer _completer = Completer(); @@ -159,12 +173,13 @@ class EventCompleter { Future get future => _completer.future; void complete() { - event.sim.observer?.onEvent( + event.sim.listener?.onEvent( name: event.eventName, time: event.sim.now, phase: EventPhase.resumed, executionHash: hashCode); _completer.complete(); event._eventCompleter = null; + SimDartHelper.removeCompleter(sim: event.sim, completer: _completer); } } diff --git a/lib/src/internal/repeat_event_action.dart b/lib/src/internal/repeat_event_action.dart index d0ef6c2..91a3ee7 100644 --- a/lib/src/internal/repeat_event_action.dart +++ b/lib/src/internal/repeat_event_action.dart @@ -45,7 +45,4 @@ class RepeatEventAction extends TimeAction { } SimDartHelper.scheduleNextAction(sim: sim); } - - @override - void dispose() {} } diff --git a/lib/src/internal/stop_action.dart b/lib/src/internal/stop_action.dart new file mode 100644 index 0000000..617ee31 --- /dev/null +++ b/lib/src/internal/stop_action.dart @@ -0,0 +1,19 @@ +import 'package:meta/meta.dart'; +import 'package:simdart/src/internal/time_action.dart'; +import 'package:simdart/src/simdart.dart'; + +@internal +class StopAction extends TimeAction{ + + StopAction({required super.start, super.order=-1, required this.sim}); + + final SimDart sim; + + @override + void execute() { + print('entrou no stopAction'); + SimDartHelper.stop(sim: sim); + SimDartHelper.scheduleNextAction(sim: sim); + } + +} \ No newline at end of file diff --git a/lib/src/internal/time_action.dart b/lib/src/internal/time_action.dart index 3d00727..e8583a8 100644 --- a/lib/src/internal/time_action.dart +++ b/lib/src/internal/time_action.dart @@ -14,6 +14,4 @@ abstract class TimeAction { final int order; void execute(); - - void dispose(); } diff --git a/lib/src/resources.dart b/lib/src/resources.dart index a774eb2..8c58667 100644 --- a/lib/src/resources.dart +++ b/lib/src/resources.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:meta/meta.dart'; import 'package:simdart/src/event_phase.dart'; import 'package:simdart/src/internal/completer_action.dart'; +import 'package:simdart/src/internal/completer_interrupt.dart'; import 'package:simdart/src/internal/event_action.dart'; import 'package:simdart/src/simdart.dart'; @@ -153,17 +154,17 @@ class ResourcesContext extends Resources { /// - Returns: A [Future] that completes when the resource is acquired. Future acquire(String name) async { if (_event.eventCompleter != null) { + //TODO method or throw? SimDartHelper.error( sim: _sim, - msg: - "This event should be waiting for the resource to be released. Did you forget to use 'await'?"); + error:StateError("This event should be waiting. Did you forget to use 'await'?")); return; } _ResourceImpl? resource = _store._map[name]; if (resource != null) { bool acquired = resource.acquire(_event); if (!acquired) { - _sim.observer?.onEvent( + _sim.listener?.onEvent( name: _event.eventName, time: _sim.now, phase: EventPhase.yielded, @@ -171,7 +172,7 @@ class ResourcesContext extends Resources { _event.buildCompleter(); resource._waiting.add(_event); SimDartHelper.scheduleNextAction(sim: _sim); - await _event.eventCompleter!.future; + await _event.eventCompleter!.future; return await acquire(name); } } @@ -185,14 +186,15 @@ class ResourcesContext extends Resources { if (resource != null) { if (resource.release(_sim, _event)) { if (resource._waiting.isNotEmpty) { - EventAction other = resource._waiting.removeAt(0); + EventAction waitingEvent = resource._waiting.removeAt(0); // Schedule a complete to resume this event in the future. SimDartHelper.addAction( sim: _sim, action: CompleterAction( start: _sim.now, - complete: other.eventCompleter!.complete, - order: other.order)); + complete: waitingEvent.eventCompleter!.complete, + order: waitingEvent.order)); + //TODO need? SimDartHelper.scheduleNextAction(sim: _sim); } } diff --git a/lib/src/sim_context.dart b/lib/src/sim_context.dart index bc16b99..13c8cf8 100644 --- a/lib/src/sim_context.dart +++ b/lib/src/sim_context.dart @@ -13,4 +13,6 @@ abstract interface class SimContext implements SimDartInterface { String get eventName; ResourcesContext get resources; + + void stop(); } diff --git a/lib/src/sim_observer.dart b/lib/src/sim_listener.dart similarity index 80% rename from lib/src/sim_observer.dart rename to lib/src/sim_listener.dart index ebde3b9..fc7ddea 100644 --- a/lib/src/sim_observer.dart +++ b/lib/src/sim_listener.dart @@ -1,7 +1,7 @@ import 'package:simdart/src/event_phase.dart'; -/// A base class for observing simulation. -abstract class SimObserver { +/// A base class for listening simulation. +abstract class SimListener { /// Called when there is a change in resource usage. /// /// [name] - The name of the resource whose usage is being reported. @@ -21,11 +21,15 @@ abstract class SimObserver { required int executionHash}); void onStart() {} + + void onError(String error); } -mixin SimObserverMixin implements SimObserver { +mixin SimListenerMixin implements SimListener { @override void onStart() {} + @override + void onError(String error) {} //@override //void onResourceUsage({required String name, required double usage}) {} @override @@ -36,8 +40,8 @@ mixin SimObserverMixin implements SimObserver { required int executionHash}) {} } -class ConsoleEventObserver with SimObserverMixin { - ConsoleEventObserver(); +class ConsoleEventListener with SimListenerMixin { + ConsoleEventListener(); @override void onEvent( @@ -47,4 +51,9 @@ class ConsoleEventObserver with SimObserverMixin { required int executionHash}) { print('[time:$time][event:$name][phase:${phase.name}]'); } + + @override + void onError(String error) { + print('Error: $error'); + } } diff --git a/lib/src/simdart.dart b/lib/src/simdart.dart index a5d96a0..879743e 100644 --- a/lib/src/simdart.dart +++ b/lib/src/simdart.dart @@ -4,15 +4,18 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:simdart/src/event.dart'; +import 'package:simdart/src/internal/completer_interrupt.dart'; +import 'package:simdart/src/internal/debug_listener.dart'; import 'package:simdart/src/internal/event_action.dart'; import 'package:simdart/src/internal/repeat_event_action.dart'; import 'package:simdart/src/internal/simdart_interface.dart'; +import 'package:simdart/src/internal/stop_action.dart'; import 'package:simdart/src/internal/time_action.dart'; import 'package:simdart/src/interval.dart'; import 'package:simdart/src/resources.dart'; import 'package:simdart/src/sim_counter.dart'; import 'package:simdart/src/sim_num.dart'; -import 'package:simdart/src/sim_observer.dart'; +import 'package:simdart/src/sim_listener.dart'; import 'package:simdart/src/sim_result.dart'; import 'package:simdart/src/start_time_handling.dart'; @@ -37,7 +40,7 @@ class SimDart implements SimDartInterface { SimDart( {this.startTimeHandling = StartTimeHandling.throwErrorIfPast, int now = 0, - this.observer, + this.listener, this.executionPriority = 0, int? seed}) : random = Random(seed), @@ -46,7 +49,9 @@ class SimDart implements SimDartInterface { RunState _runState = RunState.notStarted; RunState get runState => _runState; - final SimObserver? observer; + DebugListener? _debugListener; + + final SimListener? listener; final Map _numProperties = {}; final Map _counterProperties = {}; @@ -94,9 +99,11 @@ class SimDart implements SimDartInterface { int get now => _now; late int _now; + final List> _completerList = []; + final Completer _terminator = Completer(); - bool _error = false; + Object? _error; /// Runs the simulation, processing events in chronological order. /// @@ -108,7 +115,7 @@ class SimDart implements SimDartInterface { } _runState = RunState.running; - observer?.onStart(); + listener?.onStart(); if (until != null && _now > until) { throw ArgumentError('`now` must be less than or equal to `until`.'); } @@ -118,19 +125,39 @@ class SimDart implements SimDartInterface { if (_actions.isEmpty) { _startTime = 0; + _runState = RunState.finished; } else { _startTime = null; _scheduleNextAction(); await _terminator.future; + print('foi: $_error'); + if (_error != null) { + print('ue? erro?'); + _runState = RunState.error; + throw _error!; + } else { + _runState = RunState.finished; + } _duration = _now - (_startTime ?? 0); } - _runState = RunState.finished; return _buildResult(); } void stop() { - if (!_terminator.isCompleted) { - _terminator.complete(); + print("stop"); + _addAction(StopAction(start: now, sim:this)); + _scheduleNextAction(); + } + + void _disposeCompleteList() { + while (_completerList.isNotEmpty) { + Completer completer = _completerList.removeAt(0); + _debugListener?.onRemoveCompleter(); + if (!completer.isCompleted) { + print('vai CompleterInterrupt'); + // prevents subsequent methods from being executed after complete inside the async method. + completer.completeError(CompleterInterrupt()); + } } } @@ -207,36 +234,34 @@ class SimDart implements SimDartInterface { } void _scheduleNextAction() { - if (_error) { - return; - } + print('_scheduleNextAction'); + _debugListener?.onScheduleNextAction(); if (!_nextActionScheduled) { _nextActionScheduled = true; if (executionPriority == 0 || _executionCount < executionPriority) { _executionCount++; - Future.microtask(_consumeNextAction); + Future.microtask(_nextAction); } else { _executionCount = 0; - Future.delayed(Duration.zero, _consumeNextAction); + Future.delayed(Duration.zero, _nextAction); } } } void _addAction(TimeAction action) { - if (_error) { - return; + if (_runState==RunState.running || _runState==RunState.notStarted) { + _actions.add(action); } - _actions.add(action); } - Future _consumeNextAction() async { - if (_error || _terminator.isCompleted) { - return; - } - + Future _nextAction() async { + print('_nextAction'); + _debugListener?.onNextAction(); _nextActionScheduled = false; - if (_actions.isEmpty) { - _terminator.complete(); + if (_actions.isEmpty || runState!=RunState.running) { + if(!_terminator.isCompleted) { + _terminator.complete(); + } return; } @@ -256,11 +281,12 @@ class SimDart implements SimDartInterface { _startTime ??= now; + _debugListener?.onExecuteAction(); action.execute(); } } -enum RunState { notStarted, running, finished } +enum RunState { notStarted, running, finished, stopped, error } typedef StopCondition = bool Function(SimDart sim); @@ -274,19 +300,40 @@ class SimDartHelper { sim._addAction(action); } + static void scheduleNextAction({required SimDart sim}) { + sim._scheduleNextAction(); + } + static ResourceStore resourceStore({required SimDart sim}) => sim._resourceStore; - static void scheduleNextAction({required SimDart sim}) { - sim._scheduleNextAction(); + static void addCompleter( + {required SimDart sim, required Completer completer}) { + sim._completerList.add(completer); + sim._debugListener?.onAddCompleter(); + } + + static void removeCompleter( + {required SimDart sim, required Completer completer}) { + sim._completerList.remove(completer); + sim._debugListener?.onRemoveCompleter(); } - static void error({required SimDart sim, required String msg}) { - sim._error = true; - while (sim._actions.isNotEmpty) { - TimeAction action = sim._actions.removeFirst(); - action.dispose(); + static void error({required SimDart sim, required StateError error}) { + if(sim._error==null) { + sim._error = error; + sim._runState = RunState.error; } - sim._terminator.completeError(StateError(msg)); + sim._disposeCompleteList(); + } + + static void setDebugListener({required SimDart sim, required DebugListener? listener}) { + sim._debugListener=listener; + } + + static void stop({required SimDart sim}) { + sim._debugListener?.onStop(); + sim._runState = RunState.stopped; + sim._disposeCompleteList(); } } diff --git a/test/process_test.dart b/test/process_test.dart index 9a56a5f..56a75d5 100644 --- a/test/process_test.dart +++ b/test/process_test.dart @@ -3,10 +3,8 @@ import 'package:test/test.dart'; import 'test_helper.dart'; -Future emptyEvent(SimContext context) async {} - -Future loopEvent(SimContext context) async { - context.process(event: loopEvent); +Future emptyEvent(SimContext context) async { + print('a'); } void main() { @@ -14,31 +12,30 @@ void main() { TestHelper helper = TestHelper(); setUp(() { - helper.afterOnFinishedEvent = null; // priority to allow test timeout - sim = SimDart(executionPriority: 5, observer: helper); + sim = SimDart(executionPriority: 5, listener: helper); }); group('Process', () { test('start 1', () async { sim.process(event: emptyEvent, name: 'a'); await sim.run(); - helper.test(['[0][a][called]', '[0][a][finished]']); + helper.testEvents(['[0][a][called]', '[0][a][finished]']); }); test('start 2', () async { sim.process(event: emptyEvent, start: 10, name: 'a'); await sim.run(); - helper.test(['[10][a][called]', '[10][a][finished]']); + helper.testEvents(['[10][a][called]', '[10][a][finished]']); }); test('delay 1', () async { sim.process(event: emptyEvent, delay: 0, name: 'a'); await sim.run(); - helper.test(['[0][a][called]', '[0][a][finished]']); + helper.testEvents(['[0][a][called]', '[0][a][finished]']); }); test('delay 2', () async { sim.process(event: emptyEvent, delay: 10, name: 'a'); await sim.run(); - helper.test(['[10][a][called]', '[10][a][finished]']); + helper.testEvents(['[10][a][called]', '[10][a][finished]']); }); test('delay 3', () async { sim.process( @@ -48,7 +45,7 @@ void main() { start: 5, name: 'a'); await sim.run(); - helper.test([ + helper.testEvents([ '[5][a][called]', '[5][a][finished]', '[15][b][called]', @@ -69,7 +66,7 @@ void main() { start: 2, name: 'c'); await sim.run(); - helper.test([ + helper.testEvents([ '[0][a][called]', '[0][a][finished]', '[2][c][called]', @@ -80,11 +77,5 @@ void main() { '[10][b][finished]' ]); }); - test('stop', timeout: Timeout(Duration(seconds: 2)), () async { - helper.afterOnFinishedEvent = () => sim.stop(); - sim.process(event: loopEvent, name: 'a'); - await sim.run(); - helper.test(['[0][a][called]', '[0][a][finished]']); - }); }); } diff --git a/test/repeat_process_test.dart b/test/repeat_process_test.dart index f12021e..dc6884b 100644 --- a/test/repeat_process_test.dart +++ b/test/repeat_process_test.dart @@ -8,7 +8,7 @@ void main() { TestHelper helper = TestHelper(); setUp(() { - sim = SimDart(observer: helper); + sim = SimDart(listener: helper); }); group('Repeat process', () { @@ -19,7 +19,7 @@ void main() { interval: Interval.fixed(fixedInterval: 1, untilTime: 2)); await sim.run(); - helper.test([ + helper.testEvents([ '[0][A0][called]', '[0][A0][finished]', '[1][A1][called]', @@ -38,7 +38,7 @@ void main() { interval: Interval.fixed(fixedInterval: 1, untilTime: 2)); await sim.run(); - helper.test([ + helper.testEvents([ '[0][A0][called]', '[0][A0][yielded]', '[1][A0][resumed]', @@ -73,7 +73,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][A][called]', '[0][A][yielded]', '[0][B][called]', @@ -99,7 +99,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][A0][called]', '[0][A0][yielded]', '[1][A1][called]', @@ -134,7 +134,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][A0][called]', '[0][A0][yielded]', '[2][A0][resumed]', diff --git a/test/resource_test.dart b/test/resource_test.dart index 4fe6b9e..c77565d 100644 --- a/test/resource_test.dart +++ b/test/resource_test.dart @@ -8,7 +8,7 @@ void main() { TestHelper helper = TestHelper(); setUp(() { - sim = SimDart(observer: helper); + sim = SimDart(listener: helper); }); group('Resource', () { @@ -39,7 +39,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][A][called]', '[0][A][yielded]', '[0][B][called]', @@ -73,7 +73,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][A][called]', '[0][A][yielded]', '[0][B][called]', @@ -110,7 +110,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][A][called]', '[0][A][yielded]', '[1][B][called]', @@ -143,12 +143,7 @@ void main() { sim.process(event: (context) async {}); await sim.run(); }, - throwsA( - predicate((e) => - e is StateError && - e.message.contains( - "This event should be waiting for the resource to be released. Did you forget to use 'await'?")), - ), + throwsA(isA().having((e) => e.message, 'message', equals("This event should be waiting. Did you forget to use 'await'?"))) ); }); }); diff --git a/test/stop_test.dart b/test/stop_test.dart new file mode 100644 index 0000000..7dbb84f --- /dev/null +++ b/test/stop_test.dart @@ -0,0 +1,140 @@ +import 'package:simdart/src/simdart.dart'; +import 'package:test/test.dart'; + +import 'test_helper.dart'; + +void main() { + late SimDart sim; + TestHelper helper = TestHelper(); + + setUp(() { + sim = SimDart(listener: helper); + SimDartHelper.setDebugListener(sim: sim, listener: helper); + }); + + group('Stop', () { + test('Simple', () async { + sim.process( + event: (context) async { + print('vai chamr stop'); + context.stop(); + }, + name: 'a'); + sim.process(event: (context) async {}, name: 'b'); + + await sim.run(); + helper.testEvents(['[0][a][called]', '[0][a][finished]']); + helper.testTracks([ + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'stop', + 'scheduleNextAction', + 'nextAction' + ]); + expect(helper.completerCount, 0); + }); + + test('Wait', () async { + sim.process( + event: (context) async { + context.counter('counter').inc(); + await context.wait(10); + print('NAO FOI INTERROMPIDO'); + context.counter('counter').inc(); + }, + name: 'a'); + sim.process( + delay: 1, + event: (context) async { + context.stop(); + }, + name: 'b'); + sim.process(delay: 2, event: (context) async {}, name: 'c'); + await sim.run(); + + helper.testEvents([ + '[0][a][called]', + '[0][a][yielded]', + '[1][b][called]', + '[1][b][finished]', + '[1][a][interrupted]' + ]); + helper.testTracks([ + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'stop', + 'scheduleNextAction', + 'scheduleNextAction', + 'nextAction' + ]); + expect(sim.counter('counter').value, 1); + expect(helper.completerCount, 0); + }); + + test('Resource', () async { + sim.resources.limited(name: 'resource'); + sim.process( + event: (context) async { + await context.resources.acquire('resource'); + context.counter('counter1').inc(); + await context.wait(10); + context.counter('counter1').inc(); + context.resources.release('resource'); + }, + name: 'a'); + sim.process( + delay: 1, + event: (context) async { + context.counter('counter2').inc(); + await context.resources.acquire('resource'); + context.counter('counter2').inc(); + }, + name: 'b'); + sim.process( + delay: 2, + event: (context) async { + context.stop(); + }, + name: 'c'); + sim.process(delay: 3, event: (context) async {}, name: 'd'); + await sim.run(); + + helper.testEvents([ + '[0][a][called]', + '[0][a][yielded]', + '[1][b][called]', + '[1][b][yielded]', + '[2][c][called]', + '[2][c][finished]' + ]); + helper.testTracks([ + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'scheduleNextAction', + 'nextAction', + 'executeAction', + 'stop', + 'scheduleNextAction', + 'nextAction' + ]); + expect(sim.counter('counter1').value, 1); + expect(sim.counter('counter2').value, 1); + expect(helper.completerCount, 0); + }); + }); +} diff --git a/test/test_helper.dart b/test/test_helper.dart index 9e2e410..d68f9eb 100644 --- a/test/test_helper.dart +++ b/test/test_helper.dart @@ -1,16 +1,22 @@ import 'package:simdart/src/event_phase.dart'; -import 'package:simdart/src/sim_observer.dart'; +import 'package:simdart/src/internal/debug_listener.dart'; +import 'package:simdart/src/sim_listener.dart'; import 'package:test/expect.dart'; -class TestHelper with SimObserverMixin { +class TestHelper with SimListenerMixin implements DebugListener { final List _events = []; + final List _tracks =[]; int get length => _events.length; + int _completerCount=0; + int get completerCount=>_completerCount; - Function? afterOnFinishedEvent; + void testEvents(List events) { + expect(_events, events); + } - void test(List events) { - expect(events, _events); + void testTracks(List tracks) { + expect(_tracks, tracks); } @override @@ -20,13 +26,44 @@ class TestHelper with SimObserverMixin { required EventPhase phase, required int executionHash}) { _events.add('[$time][$name][${phase.name}]'); - if (phase == EventPhase.finished) { - afterOnFinishedEvent?.call(); - } } @override void onStart() { + _completerCount=0; _events.clear(); + _tracks.clear(); + } + + @override + void onScheduleNextAction(){ + _tracks.add('scheduleNextAction'); } + + @override + void onNextAction() { + _tracks.add('nextAction'); + } + + @override + void onExecuteAction() { + _tracks.add('executeAction'); + } + + @override + void onStop() { + _tracks.add('stop'); + } + + @override + void onAddCompleter(){ + _completerCount++; + } + + @override + void onRemoveCompleter(){ + _completerCount--; + } + + } diff --git a/test/time_loop_test.dart b/test/time_loop_test.dart index 81977ca..3fa8dd1 100644 --- a/test/time_loop_test.dart +++ b/test/time_loop_test.dart @@ -19,9 +19,6 @@ class TestAction extends TimeAction { names.add(name); SimDartHelper.scheduleNextAction(sim: sim); } - - @override - void dispose() {} } void main() { diff --git a/test/wait_test.dart b/test/wait_test.dart index eef06dd..bb25514 100644 --- a/test/wait_test.dart +++ b/test/wait_test.dart @@ -10,7 +10,7 @@ void main() { TestHelper helper = TestHelper(); setUp(() { - sim = SimDart(observer: helper); + sim = SimDart(listener: helper); }); group('Wait', () { @@ -38,7 +38,7 @@ void main() { await sim.run(); - helper.test([ + helper.testEvents([ '[0][a][called]', '[0][a][yielded]', '[10][a][resumed]', @@ -54,7 +54,7 @@ void main() { sim.process(event: emptyEvent, start: 5, name: 'b'); await sim.run(); - helper.test([ + helper.testEvents([ '[0][a][called]', '[0][a][yielded]', '[5][b][called]', @@ -74,7 +74,7 @@ void main() { sim.process(event: emptyEvent, delay: 5, name: 'b'); await sim.run(); - helper.test([ + helper.testEvents([ '[0][a][called]', '[0][a][yielded]', '[5][b][called]', @@ -98,12 +98,7 @@ void main() { await sim.run(); }, - throwsA( - predicate((e) => - e is StateError && - e.message.contains( - "Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?")), - ), + throwsA(isA().having((e) => e.message, 'message', equals("Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?"))) ); }); @@ -117,13 +112,9 @@ void main() { }, name: 'a'); await sim.run(); + print('depois'); }, - throwsA( - predicate((e) => - e is StateError && - e.message.contains( - "The event is already waiting. Did you forget to use 'await'?")), - ), + throwsA(isA().having((e) => e.message, 'message', equals("The event is already waiting. Did you forget to use 'await'?"))) ); }); }); From f631261edf8034c0c0fd8908da1385d46a042357 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Leite de Andrade Date: Sun, 2 Mar 2025 18:44:31 -0300 Subject: [PATCH 2/6] Trying to close pending Completers. #18 --- example/example.dart | 1 + lib/src/event_phase.dart | 2 -- lib/src/internal/event_action.dart | 15 ++++++--------- lib/src/simdart.dart | 19 +++++++++++++------ test/stop_test.dart | 7 ++++--- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/example/example.dart b/example/example.dart index 79d5c73..62d6525 100644 --- a/example/example.dart +++ b/example/example.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:simdart/simdart.dart'; void main() async { + final SimDart sim = SimDart(listener: ConsoleEventListener()); sim.process(event: _eventA, name: 'A'); diff --git a/lib/src/event_phase.dart b/lib/src/event_phase.dart index 133c1c5..0120b5f 100644 --- a/lib/src/event_phase.dart +++ b/lib/src/event_phase.dart @@ -11,8 +11,6 @@ enum EventPhase { yielded, - interrupted, - finished; /// Returns the string representation of the status. diff --git a/lib/src/internal/event_action.dart b/lib/src/internal/event_action.dart index a6292de..7669b7c 100644 --- a/lib/src/internal/event_action.dart +++ b/lib/src/internal/event_action.dart @@ -65,6 +65,7 @@ class EventAction extends TimeAction implements SimContext { print('then $eventName ${sim.runState.name}'); if (_eventCompleter != null && sim.runState==RunState.running) { //TODO method or throw? + print('aqui 2?'); SimDartHelper.error( sim: sim, error: @@ -79,15 +80,10 @@ class EventAction extends TimeAction implements SimContext { executionHash: hashCode); SimDartHelper.scheduleNextAction(sim: sim); }).catchError((error) { + print('ZZ $error'); //TODO rever - if(error is CompleterInterrupt) { - sim.listener?.onEvent( - name: eventName, - time: sim.now, - phase: EventPhase.interrupted, - executionHash: hashCode); - } else { - print('ZZ'); + if(error is! CompleterInterrupt) { + print('aqui 3?'); SimDartHelper.error(sim: sim, error: error); } }); @@ -97,6 +93,7 @@ class EventAction extends TimeAction implements SimContext { Future wait(int delay) async { if (_eventCompleter != null) { //TODO method or throw? + print('aqui 1?'); SimDartHelper.error( sim: sim, error: StateError("The event is already waiting. Did you forget to use 'await'?")); @@ -178,7 +175,7 @@ class EventCompleter { time: event.sim.now, phase: EventPhase.resumed, executionHash: hashCode); - _completer.complete(); + _completer.complete(); event._eventCompleter = null; SimDartHelper.removeCompleter(sim: event.sim, completer: _completer); } diff --git a/lib/src/simdart.dart b/lib/src/simdart.dart index 879743e..795b43e 100644 --- a/lib/src/simdart.dart +++ b/lib/src/simdart.dart @@ -130,7 +130,14 @@ class SimDart implements SimDartInterface { _startTime = null; _scheduleNextAction(); await _terminator.future; - print('foi: $_error'); + + try { + _disposeCompleteList(); + }catch(e){ + print(e); + } + + print('foi: ${_error.runtimeType}: $_error'); if (_error != null) { print('ue? erro?'); _runState = RunState.error; @@ -156,7 +163,7 @@ class SimDart implements SimDartInterface { if (!completer.isCompleted) { print('vai CompleterInterrupt'); // prevents subsequent methods from being executed after complete inside the async method. - completer.completeError(CompleterInterrupt()); + completer.completeError(CompleterInterrupt()); } } } @@ -234,9 +241,9 @@ class SimDart implements SimDartInterface { } void _scheduleNextAction() { - print('_scheduleNextAction'); - _debugListener?.onScheduleNextAction(); if (!_nextActionScheduled) { + print('_scheduleNextAction'); + _debugListener?.onScheduleNextAction(); _nextActionScheduled = true; if (executionPriority == 0 || _executionCount < executionPriority) { _executionCount++; @@ -320,11 +327,11 @@ class SimDartHelper { } static void error({required SimDart sim, required StateError error}) { + print('error'); if(sim._error==null) { sim._error = error; sim._runState = RunState.error; } - sim._disposeCompleteList(); } static void setDebugListener({required SimDart sim, required DebugListener? listener}) { @@ -332,8 +339,8 @@ class SimDartHelper { } static void stop({required SimDart sim}) { + print('stopando'); sim._debugListener?.onStop(); sim._runState = RunState.stopped; - sim._disposeCompleteList(); } } diff --git a/test/stop_test.dart b/test/stop_test.dart index 7dbb84f..1cd38dc 100644 --- a/test/stop_test.dart +++ b/test/stop_test.dart @@ -60,8 +60,7 @@ void main() { '[0][a][called]', '[0][a][yielded]', '[1][b][called]', - '[1][b][finished]', - '[1][a][interrupted]' + '[1][b][finished]' ]); helper.testTracks([ 'scheduleNextAction', @@ -75,7 +74,6 @@ void main() { 'executeAction', 'stop', 'scheduleNextAction', - 'scheduleNextAction', 'nextAction' ]); expect(sim.counter('counter').value, 1); @@ -128,6 +126,9 @@ void main() { 'scheduleNextAction', 'nextAction', 'executeAction', + 'scheduleNextAction', + 'nextAction', + 'executeAction', 'stop', 'scheduleNextAction', 'nextAction' From 7dd9a4ee7183f36771621f48166c163b67957007 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Leite de Andrade Date: Sun, 2 Mar 2025 20:07:00 -0300 Subject: [PATCH 3/6] Trying to close pending Completers. #18 --- lib/src/internal/event_action.dart | 15 ++++++++++----- lib/src/resources.dart | 1 + lib/src/simdart.dart | 11 ++++------- test/resource_test.dart | 3 +++ test/stop_test.dart | 8 ++++++++ 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/src/internal/event_action.dart b/lib/src/internal/event_action.dart index 7669b7c..2b36964 100644 --- a/lib/src/internal/event_action.dart +++ b/lib/src/internal/event_action.dart @@ -64,6 +64,7 @@ class EventAction extends TimeAction implements SimContext { event(this).then((_) { print('then $eventName ${sim.runState.name}'); if (_eventCompleter != null && sim.runState==RunState.running) { + SimDartHelper.removeCompleter(sim: sim, completer: _eventCompleter!.completer); //TODO method or throw? print('aqui 2?'); SimDartHelper.error( @@ -92,6 +93,7 @@ class EventAction extends TimeAction implements SimContext { @override Future wait(int delay) async { if (_eventCompleter != null) { + SimDartHelper.removeCompleter(sim: sim, completer: _eventCompleter!.completer); //TODO method or throw? print('aqui 1?'); SimDartHelper.error( @@ -149,6 +151,7 @@ class EventAction extends TimeAction implements SimContext { @override SimCounter counter(String name) { + print("counter"); return sim.counter(name); } @@ -160,14 +163,16 @@ class EventAction extends TimeAction implements SimContext { class EventCompleter { EventCompleter({required this.event}){ - SimDartHelper.addCompleter(sim: event.sim, completer: _completer); + SimDartHelper.addCompleter(sim: event.sim, completer: completer); } - final Completer _completer = Completer(); + final Completer completer = Completer(); final EventAction event; - Future get future => _completer.future; + Future get future { + return completer.future; + } void complete() { event.sim.listener?.onEvent( @@ -175,8 +180,8 @@ class EventCompleter { time: event.sim.now, phase: EventPhase.resumed, executionHash: hashCode); - _completer.complete(); + completer.complete(); event._eventCompleter = null; - SimDartHelper.removeCompleter(sim: event.sim, completer: _completer); + SimDartHelper.removeCompleter(sim: event.sim, completer: completer); } } diff --git a/lib/src/resources.dart b/lib/src/resources.dart index 8c58667..0c2433b 100644 --- a/lib/src/resources.dart +++ b/lib/src/resources.dart @@ -154,6 +154,7 @@ class ResourcesContext extends Resources { /// - Returns: A [Future] that completes when the resource is acquired. Future acquire(String name) async { if (_event.eventCompleter != null) { + SimDartHelper.removeCompleter(sim: _sim, completer: _event.eventCompleter!.completer); //TODO method or throw? SimDartHelper.error( sim: _sim, diff --git a/lib/src/simdart.dart b/lib/src/simdart.dart index 795b43e..98c760d 100644 --- a/lib/src/simdart.dart +++ b/lib/src/simdart.dart @@ -110,6 +110,7 @@ class SimDart implements SimDartInterface { /// - [until]: The time at which execution should stop. Execution will include events /// scheduled at this time (inclusive). If null, execution will continue indefinitely. Future run({int? until}) async { + if (runState != RunState.notStarted) { throw StateError('Simulation has already started.'); } @@ -131,13 +132,8 @@ class SimDart implements SimDartInterface { _scheduleNextAction(); await _terminator.future; - try { - _disposeCompleteList(); - }catch(e){ - print(e); - } - - print('foi: ${_error.runtimeType}: $_error'); + print('terminou'); + print('foi com erro? -> ${_error.runtimeType}: $_error'); if (_error != null) { print('ue? erro?'); _runState = RunState.error; @@ -266,6 +262,7 @@ class SimDart implements SimDartInterface { _debugListener?.onNextAction(); _nextActionScheduled = false; if (_actions.isEmpty || runState!=RunState.running) { + _disposeCompleteList(); if(!_terminator.isCompleted) { _terminator.complete(); } diff --git a/test/resource_test.dart b/test/resource_test.dart index c77565d..3cbb228 100644 --- a/test/resource_test.dart +++ b/test/resource_test.dart @@ -129,6 +129,9 @@ void main() { '[20][C][finished]' ]); }); + + + test('without await', () async { expect( () async { diff --git a/test/stop_test.dart b/test/stop_test.dart index 1cd38dc..2f25c7e 100644 --- a/test/stop_test.dart +++ b/test/stop_test.dart @@ -1,8 +1,12 @@ +import 'dart:async'; + import 'package:simdart/src/simdart.dart'; import 'package:test/test.dart'; import 'test_helper.dart'; + + void main() { late SimDart sim; TestHelper helper = TestHelper(); @@ -12,6 +16,8 @@ void main() { SimDartHelper.setDebugListener(sim: sim, listener: helper); }); + + group('Stop', () { test('Simple', () async { sim.process( @@ -76,8 +82,10 @@ void main() { 'scheduleNextAction', 'nextAction' ]); + print("counter valor:${sim.counter('counter').value} "); expect(sim.counter('counter').value, 1); expect(helper.completerCount, 0); + print('---------------'); }); test('Resource', () async { From c3c80cd4d093033d300fb3a5ee4c0d006d139a0b Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Leite de Andrade Date: Sun, 2 Mar 2025 21:25:41 -0300 Subject: [PATCH 4/6] Removing nextAction debug track. #18 --- lib/src/internal/debug_listener.dart | 2 -- lib/src/resources.dart | 1 - lib/src/simdart.dart | 7 ++----- test/stop_test.dart | 18 +++--------------- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/lib/src/internal/debug_listener.dart b/lib/src/internal/debug_listener.dart index 8b2ad12..2e25249 100644 --- a/lib/src/internal/debug_listener.dart +++ b/lib/src/internal/debug_listener.dart @@ -5,8 +5,6 @@ abstract class DebugListener { void onScheduleNextAction(); - void onNextAction(); - void onExecuteAction(); void onStop(); diff --git a/lib/src/resources.dart b/lib/src/resources.dart index 0c2433b..1d8f873 100644 --- a/lib/src/resources.dart +++ b/lib/src/resources.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'package:meta/meta.dart'; import 'package:simdart/src/event_phase.dart'; import 'package:simdart/src/internal/completer_action.dart'; -import 'package:simdart/src/internal/completer_interrupt.dart'; import 'package:simdart/src/internal/event_action.dart'; import 'package:simdart/src/simdart.dart'; diff --git a/lib/src/simdart.dart b/lib/src/simdart.dart index 98c760d..bd58b86 100644 --- a/lib/src/simdart.dart +++ b/lib/src/simdart.dart @@ -149,10 +149,9 @@ class SimDart implements SimDartInterface { void stop() { print("stop"); _addAction(StopAction(start: now, sim:this)); - _scheduleNextAction(); } - void _disposeCompleteList() { + void _disposeCompleterList() { while (_completerList.isNotEmpty) { Completer completer = _completerList.removeAt(0); _debugListener?.onRemoveCompleter(); @@ -258,11 +257,9 @@ class SimDart implements SimDartInterface { } Future _nextAction() async { - print('_nextAction'); - _debugListener?.onNextAction(); _nextActionScheduled = false; if (_actions.isEmpty || runState!=RunState.running) { - _disposeCompleteList(); + _disposeCompleterList(); if(!_terminator.isCompleted) { _terminator.complete(); } diff --git a/test/stop_test.dart b/test/stop_test.dart index 2f25c7e..ed1f2fc 100644 --- a/test/stop_test.dart +++ b/test/stop_test.dart @@ -32,14 +32,11 @@ void main() { helper.testEvents(['[0][a][called]', '[0][a][finished]']); helper.testTracks([ 'scheduleNextAction', - 'nextAction', 'executeAction', 'scheduleNextAction', - 'nextAction', 'executeAction', 'stop', - 'scheduleNextAction', - 'nextAction' + 'scheduleNextAction' ]); expect(helper.completerCount, 0); }); @@ -70,17 +67,13 @@ void main() { ]); helper.testTracks([ 'scheduleNextAction', - 'nextAction', 'executeAction', 'scheduleNextAction', - 'nextAction', 'executeAction', 'scheduleNextAction', - 'nextAction', 'executeAction', 'stop', - 'scheduleNextAction', - 'nextAction' + 'scheduleNextAction' ]); print("counter valor:${sim.counter('counter').value} "); expect(sim.counter('counter').value, 1); @@ -126,20 +119,15 @@ void main() { ]); helper.testTracks([ 'scheduleNextAction', - 'nextAction', 'executeAction', 'scheduleNextAction', - 'nextAction', 'executeAction', 'scheduleNextAction', - 'nextAction', 'executeAction', 'scheduleNextAction', - 'nextAction', 'executeAction', 'stop', - 'scheduleNextAction', - 'nextAction' + 'scheduleNextAction' ]); expect(sim.counter('counter1').value, 1); expect(sim.counter('counter2').value, 1); From beae09f8f8ca2b1ee90fe66638de612843707470 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Leite de Andrade Date: Mon, 3 Mar 2025 14:10:06 -0300 Subject: [PATCH 5/6] Adding error handling. #18 --- CHANGELOG.md | 1 + example/example.dart | 1 - lib/src/internal/completer_interrupt.dart | 4 +- lib/src/internal/debug_listener.dart | 8 -- lib/src/internal/event_action.dart | 60 +++++++------- lib/src/internal/stop_action.dart | 9 +-- lib/src/resources.dart | 8 +- lib/src/simdart.dart | 36 +++------ pubspec.yaml | 2 +- test/error_test.dart | 97 +++++++++++++++++++++++ test/process_test.dart | 4 +- test/resource_test.dart | 34 ++++---- test/stop_test.dart | 40 ---------- test/test_helper.dart | 38 ++------- test/wait_test.dart | 63 +++++++++------ 15 files changed, 208 insertions(+), 197 deletions(-) create mode 100644 test/error_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index cded398..7d11090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Added * `stop` method in `SimContext`. + * Error handling. ## 0.3.0 diff --git a/example/example.dart b/example/example.dart index 62d6525..79d5c73 100644 --- a/example/example.dart +++ b/example/example.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:simdart/simdart.dart'; void main() async { - final SimDart sim = SimDart(listener: ConsoleEventListener()); sim.process(event: _eventA, name: 'A'); diff --git a/lib/src/internal/completer_interrupt.dart b/lib/src/internal/completer_interrupt.dart index 31a533f..d1dea51 100644 --- a/lib/src/internal/completer_interrupt.dart +++ b/lib/src/internal/completer_interrupt.dart @@ -1,6 +1,4 @@ import 'package:meta/meta.dart'; @internal -class CompleterInterrupt{ - -} \ No newline at end of file +class CompleterInterrupt {} diff --git a/lib/src/internal/debug_listener.dart b/lib/src/internal/debug_listener.dart index 2e25249..21845bf 100644 --- a/lib/src/internal/debug_listener.dart +++ b/lib/src/internal/debug_listener.dart @@ -2,15 +2,7 @@ import 'package:meta/meta.dart'; @internal abstract class DebugListener { - - void onScheduleNextAction(); - - void onExecuteAction(); - - void onStop(); - void onAddCompleter(); void onRemoveCompleter(); } - diff --git a/lib/src/internal/event_action.dart b/lib/src/internal/event_action.dart index 2b36964..f754556 100644 --- a/lib/src/internal/event_action.dart +++ b/lib/src/internal/event_action.dart @@ -50,7 +50,6 @@ class EventAction extends TimeAction implements SimContext { @override void execute() { - print('execute: $eventName'); if (_eventCompleter != null) { throw StateError('This event is yielding'); } @@ -62,30 +61,25 @@ class EventAction extends TimeAction implements SimContext { executionHash: hashCode); event(this).then((_) { - print('then $eventName ${sim.runState.name}'); - if (_eventCompleter != null && sim.runState==RunState.running) { - SimDartHelper.removeCompleter(sim: sim, completer: _eventCompleter!.completer); - //TODO method or throw? - print('aqui 2?'); - SimDartHelper.error( - sim: sim, - error: - StateError( - "Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?")); - return; - } - sim.listener?.onEvent( - name: eventName, - time: sim.now, - phase: EventPhase.finished, - executionHash: hashCode); - SimDartHelper.scheduleNextAction(sim: sim); + if (_eventCompleter != null && sim.runState == RunState.running) { + SimDartHelper.removeCompleter( + sim: sim, completer: _eventCompleter!.completer); + SimDartHelper.error( + sim: sim, + error: StateError( + "Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?")); + return; + } + sim.listener?.onEvent( + name: eventName, + time: sim.now, + phase: EventPhase.finished, + executionHash: hashCode); + SimDartHelper.scheduleNextAction(sim: sim); }).catchError((error) { - print('ZZ $error'); - //TODO rever - if(error is! CompleterInterrupt) { - print('aqui 3?'); + if (error is! CompleterInterrupt) { SimDartHelper.error(sim: sim, error: error); + SimDartHelper.scheduleNextAction(sim: sim); } }); } @@ -93,12 +87,12 @@ class EventAction extends TimeAction implements SimContext { @override Future wait(int delay) async { if (_eventCompleter != null) { - SimDartHelper.removeCompleter(sim: sim, completer: _eventCompleter!.completer); - //TODO method or throw? - print('aqui 1?'); + SimDartHelper.removeCompleter( + sim: sim, completer: _eventCompleter!.completer); SimDartHelper.error( sim: sim, - error: StateError("The event is already waiting. Did you forget to use 'await'?")); + error: StateError( + "The event is already waiting. Did you forget to use 'await'?")); return; } @@ -119,7 +113,7 @@ class EventAction extends TimeAction implements SimContext { order: order)); SimDartHelper.scheduleNextAction(sim: sim); - await _eventCompleter!.future; + await _eventCompleter!.future; } @override @@ -145,13 +139,13 @@ class EventAction extends TimeAction implements SimContext { } @override - void stop(){ - SimDartHelper.addAction(sim: sim, action: StopAction(start: sim.now, sim:sim)); + void stop() { + SimDartHelper.addAction( + sim: sim, action: StopAction(start: sim.now, sim: sim)); } @override SimCounter counter(String name) { - print("counter"); return sim.counter(name); } @@ -162,7 +156,7 @@ class EventAction extends TimeAction implements SimContext { } class EventCompleter { - EventCompleter({required this.event}){ + EventCompleter({required this.event}) { SimDartHelper.addCompleter(sim: event.sim, completer: completer); } @@ -180,7 +174,7 @@ class EventCompleter { time: event.sim.now, phase: EventPhase.resumed, executionHash: hashCode); - completer.complete(); + completer.complete(); event._eventCompleter = null; SimDartHelper.removeCompleter(sim: event.sim, completer: completer); } diff --git a/lib/src/internal/stop_action.dart b/lib/src/internal/stop_action.dart index 617ee31..b14162c 100644 --- a/lib/src/internal/stop_action.dart +++ b/lib/src/internal/stop_action.dart @@ -3,17 +3,14 @@ import 'package:simdart/src/internal/time_action.dart'; import 'package:simdart/src/simdart.dart'; @internal -class StopAction extends TimeAction{ - - StopAction({required super.start, super.order=-1, required this.sim}); +class StopAction extends TimeAction { + StopAction({required super.start, super.order = -1, required this.sim}); final SimDart sim; @override void execute() { - print('entrou no stopAction'); SimDartHelper.stop(sim: sim); SimDartHelper.scheduleNextAction(sim: sim); } - -} \ No newline at end of file +} diff --git a/lib/src/resources.dart b/lib/src/resources.dart index 1d8f873..ce01e65 100644 --- a/lib/src/resources.dart +++ b/lib/src/resources.dart @@ -153,11 +153,13 @@ class ResourcesContext extends Resources { /// - Returns: A [Future] that completes when the resource is acquired. Future acquire(String name) async { if (_event.eventCompleter != null) { - SimDartHelper.removeCompleter(sim: _sim, completer: _event.eventCompleter!.completer); + SimDartHelper.removeCompleter( + sim: _sim, completer: _event.eventCompleter!.completer); //TODO method or throw? SimDartHelper.error( sim: _sim, - error:StateError("This event should be waiting. Did you forget to use 'await'?")); + error: StateError( + "This event should be waiting. Did you forget to use 'await'?")); return; } _ResourceImpl? resource = _store._map[name]; @@ -172,7 +174,7 @@ class ResourcesContext extends Resources { _event.buildCompleter(); resource._waiting.add(_event); SimDartHelper.scheduleNextAction(sim: _sim); - await _event.eventCompleter!.future; + await _event.eventCompleter!.future; return await acquire(name); } } diff --git a/lib/src/simdart.dart b/lib/src/simdart.dart index bd58b86..d93d9c7 100644 --- a/lib/src/simdart.dart +++ b/lib/src/simdart.dart @@ -49,7 +49,7 @@ class SimDart implements SimDartInterface { RunState _runState = RunState.notStarted; RunState get runState => _runState; - DebugListener? _debugListener; + DebugListener? _debugListener; final SimListener? listener; @@ -110,7 +110,6 @@ class SimDart implements SimDartInterface { /// - [until]: The time at which execution should stop. Execution will include events /// scheduled at this time (inclusive). If null, execution will continue indefinitely. Future run({int? until}) async { - if (runState != RunState.notStarted) { throw StateError('Simulation has already started.'); } @@ -131,11 +130,7 @@ class SimDart implements SimDartInterface { _startTime = null; _scheduleNextAction(); await _terminator.future; - - print('terminou'); - print('foi com erro? -> ${_error.runtimeType}: $_error'); if (_error != null) { - print('ue? erro?'); _runState = RunState.error; throw _error!; } else { @@ -147,8 +142,7 @@ class SimDart implements SimDartInterface { } void stop() { - print("stop"); - _addAction(StopAction(start: now, sim:this)); + _addAction(StopAction(start: now, sim: this)); } void _disposeCompleterList() { @@ -156,9 +150,8 @@ class SimDart implements SimDartInterface { Completer completer = _completerList.removeAt(0); _debugListener?.onRemoveCompleter(); if (!completer.isCompleted) { - print('vai CompleterInterrupt'); // prevents subsequent methods from being executed after complete inside the async method. - completer.completeError(CompleterInterrupt()); + completer.completeError(CompleterInterrupt()); } } } @@ -237,8 +230,6 @@ class SimDart implements SimDartInterface { void _scheduleNextAction() { if (!_nextActionScheduled) { - print('_scheduleNextAction'); - _debugListener?.onScheduleNextAction(); _nextActionScheduled = true; if (executionPriority == 0 || _executionCount < executionPriority) { _executionCount++; @@ -251,16 +242,16 @@ class SimDart implements SimDartInterface { } void _addAction(TimeAction action) { - if (_runState==RunState.running || _runState==RunState.notStarted) { + if (_runState == RunState.running || _runState == RunState.notStarted) { _actions.add(action); } } Future _nextAction() async { _nextActionScheduled = false; - if (_actions.isEmpty || runState!=RunState.running) { + if (_actions.isEmpty || runState != RunState.running) { _disposeCompleterList(); - if(!_terminator.isCompleted) { + if (!_terminator.isCompleted) { _terminator.complete(); } return; @@ -282,7 +273,6 @@ class SimDart implements SimDartInterface { _startTime ??= now; - _debugListener?.onExecuteAction(); action.execute(); } } @@ -320,21 +310,19 @@ class SimDartHelper { sim._debugListener?.onRemoveCompleter(); } - static void error({required SimDart sim, required StateError error}) { - print('error'); - if(sim._error==null) { + static void error({required SimDart sim, required Object error}) { + if (sim._error == null) { sim._error = error; sim._runState = RunState.error; } } - - static void setDebugListener({required SimDart sim, required DebugListener? listener}) { - sim._debugListener=listener; + + static void setDebugListener( + {required SimDart sim, required DebugListener? listener}) { + sim._debugListener = listener; } static void stop({required SimDart sim}) { - print('stopando'); - sim._debugListener?.onStop(); sim._runState = RunState.stopped; } } diff --git a/pubspec.yaml b/pubspec.yaml index 8247363..7e2d621 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: simdart description: A discrete event simulation package for Dart, designed for modeling and analyzing processes and systems. -version: 0.3.0 +version: 0.4.0 repository: https://github.com/SimDart/simdart topics: diff --git a/test/error_test.dart b/test/error_test.dart new file mode 100644 index 0000000..e77a9b4 --- /dev/null +++ b/test/error_test.dart @@ -0,0 +1,97 @@ +import 'dart:async'; + +import 'package:simdart/src/simdart.dart'; +import 'package:test/test.dart'; + +import 'test_helper.dart'; + +void main() { + late SimDart sim; + TestHelper helper = TestHelper(); + + setUp(() { + sim = SimDart(listener: helper); + SimDartHelper.setDebugListener(sim: sim, listener: helper); + }); + + group('Error', () { + test('Simple', () async { + sim.process( + event: (context) async { + throw 'ERROR'; + }, + name: 'a'); + sim.process(event: (context) async {}, name: 'b'); + + await expectLater(sim.run(), throwsA(equals('ERROR'))); + + helper.testEvents(['[0][a][called]']); + expect(helper.completerCount, 0); + }); + + test('Wait', () async { + sim.process( + event: (context) async { + context.counter('counter').inc(); + await context.wait(10); + context.counter('counter').inc(); + }, + name: 'a'); + sim.process( + delay: 1, + event: (context) async { + throw 'ERROR'; + }, + name: 'b'); + sim.process(delay: 2, event: (context) async {}, name: 'c'); + + await expectLater(sim.run(), throwsA(equals('ERROR'))); + + helper + .testEvents(['[0][a][called]', '[0][a][yielded]', '[1][b][called]']); + expect(sim.counter('counter').value, 1); + expect(helper.completerCount, 0); + }); + + test('Resource', () async { + sim.resources.limited(name: 'resource'); + sim.process( + event: (context) async { + await context.resources.acquire('resource'); + context.counter('counter1').inc(); + await context.wait(10); + context.counter('counter1').inc(); + context.resources.release('resource'); + }, + name: 'a'); + sim.process( + delay: 1, + event: (context) async { + context.counter('counter2').inc(); + await context.resources.acquire('resource'); + context.counter('counter2').inc(); + }, + name: 'b'); + sim.process( + delay: 2, + event: (context) async { + throw 'ERROR'; + }, + name: 'c'); + sim.process(delay: 3, event: (context) async {}, name: 'd'); + + await expectLater(sim.run(), throwsA(equals('ERROR'))); + + helper.testEvents([ + '[0][a][called]', + '[0][a][yielded]', + '[1][b][called]', + '[1][b][yielded]', + '[2][c][called]' + ]); + expect(sim.counter('counter1').value, 1); + expect(sim.counter('counter2').value, 1); + expect(helper.completerCount, 0); + }); + }); +} diff --git a/test/process_test.dart b/test/process_test.dart index 56a75d5..f8d69b0 100644 --- a/test/process_test.dart +++ b/test/process_test.dart @@ -3,9 +3,7 @@ import 'package:test/test.dart'; import 'test_helper.dart'; -Future emptyEvent(SimContext context) async { - print('a'); -} +Future emptyEvent(SimContext context) async {} void main() { late SimDart sim; diff --git a/test/resource_test.dart b/test/resource_test.dart index 3cbb228..a2c19cc 100644 --- a/test/resource_test.dart +++ b/test/resource_test.dart @@ -130,24 +130,24 @@ void main() { ]); }); - - test('without await', () async { - expect( - () async { - sim.resources.limited(name: 'r', capacity: 1); - sim.process( - event: (context) async { - context.resources.acquire('r'); // acquired - context.resources.acquire('r'); // should await - context.resources.acquire('r'); // error - }, - name: 'a'); - sim.process(event: (context) async {}); - await sim.run(); - }, - throwsA(isA().having((e) => e.message, 'message', equals("This event should be waiting. Did you forget to use 'await'?"))) - ); + expect(() async { + sim.resources.limited(name: 'r', capacity: 1); + sim.process( + event: (context) async { + context.resources.acquire('r'); // acquired + context.resources.acquire('r'); // should await + context.resources.acquire('r'); // error + }, + name: 'a'); + sim.process(event: (context) async {}); + await sim.run(); + }, + throwsA(isA().having( + (e) => e.message, + 'message', + equals( + "This event should be waiting. Did you forget to use 'await'?")))); }); }); } diff --git a/test/stop_test.dart b/test/stop_test.dart index ed1f2fc..d9a2b86 100644 --- a/test/stop_test.dart +++ b/test/stop_test.dart @@ -1,12 +1,8 @@ -import 'dart:async'; - import 'package:simdart/src/simdart.dart'; import 'package:test/test.dart'; import 'test_helper.dart'; - - void main() { late SimDart sim; TestHelper helper = TestHelper(); @@ -16,13 +12,10 @@ void main() { SimDartHelper.setDebugListener(sim: sim, listener: helper); }); - - group('Stop', () { test('Simple', () async { sim.process( event: (context) async { - print('vai chamr stop'); context.stop(); }, name: 'a'); @@ -30,14 +23,6 @@ void main() { await sim.run(); helper.testEvents(['[0][a][called]', '[0][a][finished]']); - helper.testTracks([ - 'scheduleNextAction', - 'executeAction', - 'scheduleNextAction', - 'executeAction', - 'stop', - 'scheduleNextAction' - ]); expect(helper.completerCount, 0); }); @@ -46,7 +31,6 @@ void main() { event: (context) async { context.counter('counter').inc(); await context.wait(10); - print('NAO FOI INTERROMPIDO'); context.counter('counter').inc(); }, name: 'a'); @@ -65,20 +49,8 @@ void main() { '[1][b][called]', '[1][b][finished]' ]); - helper.testTracks([ - 'scheduleNextAction', - 'executeAction', - 'scheduleNextAction', - 'executeAction', - 'scheduleNextAction', - 'executeAction', - 'stop', - 'scheduleNextAction' - ]); - print("counter valor:${sim.counter('counter').value} "); expect(sim.counter('counter').value, 1); expect(helper.completerCount, 0); - print('---------------'); }); test('Resource', () async { @@ -117,18 +89,6 @@ void main() { '[2][c][called]', '[2][c][finished]' ]); - helper.testTracks([ - 'scheduleNextAction', - 'executeAction', - 'scheduleNextAction', - 'executeAction', - 'scheduleNextAction', - 'executeAction', - 'scheduleNextAction', - 'executeAction', - 'stop', - 'scheduleNextAction' - ]); expect(sim.counter('counter1').value, 1); expect(sim.counter('counter2').value, 1); expect(helper.completerCount, 0); diff --git a/test/test_helper.dart b/test/test_helper.dart index d68f9eb..280c5b4 100644 --- a/test/test_helper.dart +++ b/test/test_helper.dart @@ -5,20 +5,15 @@ import 'package:test/expect.dart'; class TestHelper with SimListenerMixin implements DebugListener { final List _events = []; - final List _tracks =[]; int get length => _events.length; - int _completerCount=0; - int get completerCount=>_completerCount; + int _completerCount = 0; + int get completerCount => _completerCount; void testEvents(List events) { expect(_events, events); } - void testTracks(List tracks) { - expect(_tracks, tracks); - } - @override void onEvent( {required String name, @@ -30,40 +25,17 @@ class TestHelper with SimListenerMixin implements DebugListener { @override void onStart() { - _completerCount=0; + _completerCount = 0; _events.clear(); - _tracks.clear(); - } - - @override - void onScheduleNextAction(){ - _tracks.add('scheduleNextAction'); - } - - @override - void onNextAction() { - _tracks.add('nextAction'); } @override - void onExecuteAction() { - _tracks.add('executeAction'); - } - - @override - void onStop() { - _tracks.add('stop'); - } - - @override - void onAddCompleter(){ + void onAddCompleter() { _completerCount++; } @override - void onRemoveCompleter(){ + void onRemoveCompleter() { _completerCount--; } - - } diff --git a/test/wait_test.dart b/test/wait_test.dart index bb25514..890120a 100644 --- a/test/wait_test.dart +++ b/test/wait_test.dart @@ -87,35 +87,48 @@ void main() { }); test('wait without await', () async { - expect( - () async { - sim.process( - event: (context) async { - context.wait(10); - }, - name: 'a'); - sim.process(event: emptyEvent, start: 5, name: 'b'); + sim.process( + event: (context) async { + context.wait(10); + }, + name: 'a'); + sim.process(event: emptyEvent, start: 5, name: 'b'); + sim.process(event: emptyEvent, start: 10, name: 'c'); - await sim.run(); - }, - throwsA(isA().having((e) => e.message, 'message', equals("Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?"))) - ); + await expectLater( + sim.run(), + throwsA(isA().having( + (e) => e.message, + 'message', + equals( + "Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?")))); + helper.testEvents([ + '[0][a][called]', + '[0][a][yielded]', + '[5][b][called]', + '[5][b][finished]' + ]); + expect(helper.completerCount, 0); }); test('multiple wait without await', () async { - expect( - () async { - sim.process( - event: (context) async { - context.wait(10); - context.wait(10); - }, - name: 'a'); - await sim.run(); - print('depois'); - }, - throwsA(isA().having((e) => e.message, 'message', equals("The event is already waiting. Did you forget to use 'await'?"))) - ); + sim.process( + event: (context) async { + context.wait(10); + context.wait(10); + }, + name: 'a'); + sim.process(event: emptyEvent, start: 5, name: 'b'); + await expectLater( + sim.run(), + throwsA(isA().having( + (e) => e.message, + 'message', + equals( + "The event is already waiting. Did you forget to use 'await'?")))); + helper.testEvents( + ['[0][a][called]', '[0][a][yielded]', '[0][a][finished]']); + expect(helper.completerCount, 0); }); }); } From 52ea775cad80cfa5a0a10f61549499366a9fe662 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Leite de Andrade Date: Mon, 3 Mar 2025 14:12:04 -0300 Subject: [PATCH 6/6] Removing unused import. --- test/error_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/error_test.dart b/test/error_test.dart index e77a9b4..58163a1 100644 --- a/test/error_test.dart +++ b/test/error_test.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:simdart/src/simdart.dart'; import 'package:test/test.dart';