Add testsw
diff --git a/lib/src/process_pool.dart b/lib/src/process_pool.dart index ada6658..59172a4 100644 --- a/lib/src/process_pool.dart +++ b/lib/src/process_pool.dart
@@ -7,10 +7,13 @@ import 'dart:io' show Directory, Platform, stdout, SystemEncoding, stderr, ProcessStartMode; import 'package:async/async.dart' show StreamGroup; +import 'package:process_runner/process_runner.dart'; import 'process_runner.dart'; abstract class DependentJob { + DependentJob({Iterable<DependentJob> dependsOn = const <DependentJob>{}}) : _dependsOn = dependsOn.toSet(); + /// The name of the job. String get name; @@ -19,11 +22,45 @@ /// This job will not be scheduled until all of the jobs in this set have /// completed. /// + /// To add a dependency, call [addDependency] or [addDependencies]. + /// + /// To remove a dependency, call [removeDependency] or [removeDependencies]. + /// + /// Modifying the returned set will not affect the dependencies of this job. + /// /// Will throw if there is a dependency cycle, or if the given job has not /// been added to the pool. /// /// Defaults to an empty set. - Set<DependentJob> get dependsOn; + Set<DependentJob> get dependsOn => _dependsOn.toSet(); + final Set<DependentJob> _dependsOn; + + void addDependency(DependentJob job) { + if (job == this) { + throw ProcessRunnerException('A job cannot depend on itself'); + } + if (_dependsOn.contains(job)) { + throw ProcessRunnerException('$job is already a dependency of $this'); + } + if (job._dependsOn.contains(this)) { + throw ProcessRunnerException('$this is already a dependency of $job, no cycle allowed'); + } + _dependsOn.add(job); + } + + void removeDependency(DependentJob job) { + assert(_dependsOn.contains(job)); + assert(job != this); + _dependsOn.remove(job); + } + + void addDependencies(Iterable<DependentJob> jobs) { + jobs.forEach(addDependency); + } + + void removeDependencies(Iterable<DependentJob> jobs) { + jobs.forEach(removeDependency); + } void addToQueue(List<DependentJob> jobs); } @@ -44,7 +81,7 @@ this.runInShell = false, Iterable<DependentJob>? dependsOn, }) : name = name ?? command.join(' '), - dependsOn = dependsOn?.toSet() ?? <DependentJob>{}; + super(dependsOn: dependsOn?.toSet() ?? <DependentJob>{}); /// The name of the job. /// @@ -124,24 +161,18 @@ } @override - Set<DependentJob> dependsOn; - - @override String toString() => '${command.join(' ')} with ${dependsOn.length} dependencies'; } class WorkerJobGroup extends DependentJob { - WorkerJobGroup( - this.workers, { - this.name = '<unknown>', - bool setDependencies = true, - }) : dependsOn = workers.toSet(), - assert(workers.isNotEmpty) { - // Make sure they run in series. - if (setDependencies) { - for (int i = 1; i < workers.length; i++) { - workers[i].dependsOn.add(workers[i - 1]); - } + WorkerJobGroup(Iterable<DependentJob> workers, {Iterable<DependentJob>? dependsOn, this.name = 'Group'}) + : assert(workers.isNotEmpty), + workers = <DependentJob>[...workers], + super(dependsOn: <DependentJob>{...workers.toSet(), if (dependsOn != null) ...dependsOn}) { + // Make sure they run in series, and they depend on anything that the group + // depends on. + for (int i = 1; i < workers.length; i++) { + this.workers[i].addDependency(this.workers[i - 1]); } } @@ -152,7 +183,20 @@ final List<DependentJob> workers; @override - final Set<DependentJob> dependsOn; + void addDependency(DependentJob job) { + for (final DependentJob worker in workers) { + worker.addDependency(job); + } + super.addDependency(job); + } + + @override + void removeDependency(DependentJob job) { + for (final DependentJob worker in workers) { + worker.removeDependency(job); + } + super.removeDependency(job); + } @override void addToQueue(List<DependentJob> jobs) {
diff --git a/test/src/process_pool_test.dart b/test/src/process_pool_test.dart index 5bbf9f3..6a5903a 100644 --- a/test/src/process_pool_test.dart +++ b/test/src/process_pool_test.dart
@@ -198,7 +198,7 @@ }; fakeProcessManager.fakeResults = calls; final WorkerJobGroup groupA = WorkerJobGroup( - <WorkerJob>[ + <DependentJob>[ WorkerJob(<String>['commandA1', 'arg1', 'arg2'], name: 'job A1'), WorkerJob(<String>['commandA2', 'arg1', 'arg2'], name: 'job A2'), WorkerJob(<String>['commandA3', 'arg1', 'arg2'], name: 'job A3'), @@ -206,14 +206,14 @@ name: 'Group A', ); final WorkerJobGroup groupB = WorkerJobGroup( - <WorkerJob>[ + <DependentJob>[ WorkerJob(<String>['commandB1', 'arg1', 'arg2'], name: 'job B1'), WorkerJob(<String>['commandB2', 'arg1', 'arg2'], name: 'job B2'), WorkerJob(<String>['commandB3', 'arg1', 'arg2'], name: 'job B3'), ], name: 'Group B', ); - groupB.dependsOn.add(groupA); + groupB.addDependency(groupA); final List<DependentJob> jobs = <DependentJob>[groupA, groupB]; final List<WorkerJob> completed = await processPool.runToCompletion(jobs); expect(completed.length, equals(6)); @@ -233,19 +233,26 @@ completed.where((WorkerJob job) => job.result.exitCode == 0).map((WorkerJob job) => job.name), unorderedEquals(<String>['job B1', 'job A1', 'job A2', 'job A3']), ); - // Either group A or B can come first, but the individual group tasks should - // be in order. - expect( - <String>[completed[0].name, completed[1].name], - unorderedEquals(<String>['job A1', 'job B1']), - ); - expect( - <String>[completed[2].name, completed[3].name], - unorderedEquals(<String>['job A2', 'job B2']), - ); - expect( - <String>[completed[4].name, completed[5].name], - unorderedEquals(<String>['job A3', 'job B3']), - ); + }); +test("Jobs can't depend on themselves", () async { + fakeProcessManager = FakeProcessManager((String value) {}); + processRunner = ProcessRunner(processManager: fakeProcessManager); + processPool = ProcessPool(processRunner: processRunner, printReport: null); + final Map<FakeInvocationRecord, List<ProcessResult>> calls = <FakeInvocationRecord, List<ProcessResult>>{ + FakeInvocationRecord(<String>['commandA1', 'arg1', 'arg2'], testPath): <ProcessResult>[ + ProcessResult(0, 0, 'outputA1', 'stderrA1'), + ], + }; + fakeProcessManager.fakeResults = calls; + final WorkerJob job = WorkerJob(<String>['commandA1', 'arg1', 'arg2'], name: 'job A1'); + + ProcessRunnerException? exception; + try { + job.addDependency(job); + } on ProcessRunnerException catch (e) { + exception = e; + } + expect(exception, isNotNull); + expect(exception!.message, equals('A job cannot depend on itself')); }); }