Removing blocking read from StdioStepper.
Add additional commands to stepper.
General cleanup.
diff --git a/lib/support/stdio_stepper.dart b/lib/support/stdio_stepper.dart
index bb82918..cf44305 100644
--- a/lib/support/stdio_stepper.dart
+++ b/lib/support/stdio_stepper.dart
@@ -14,23 +14,37 @@
library webdriver.support.stdio_stepper;
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io';
+import 'dart:async' show Future, Stream, StreamController;
+import 'dart:convert' show Encoding, JSON;
+import 'dart:io' show exit, Stdin, stdin, SYSTEM_ENCODING;
import 'package:webdriver/src/stepper.dart';
+LineReader _stdinLineReader;
+
+/// A [LineReader] instance connected to 'dart:io' [stdin].
+LineReader get stdinLineReader {
+ if (_stdinLineReader == null) {
+ _stdinLineReader = new LineReader(stdin);
+ }
+ return _stdinLineReader;
+}
+
/// Provides a command line interface for stepping through or skipping
/// WebDriver commands.
class StdioStepper implements Stepper {
- StdioStepper() {
- stdin.lineMode = true;
- }
+ bool enabled = true;
+ final LineReader _reader;
+
+ StdioStepper({LineReader reader})
+ : _reader = reader == null ? stdinLineReader : reader;
+
+ @override
Future<bool> step(String method, String command, params) async {
+ if (!enabled) return true;
print('$method $command(${JSON.encode(params)}):');
- while (true) {
- var command = stdin.readLineSync(retainNewlines: false).trim();
+ await for (String command in _reader.onLine) {
switch (command) {
case 'continue':
case 'c':
@@ -45,16 +59,76 @@
case 'h':
_printUsage();
break;
+ case 'disable':
+ case 'd':
+ enabled = false;
+ return true;
+ case 'quit':
+ case 'q':
+ exit(-1);
+ return false;
default:
print('invalid command: `$command` enter `h` or `help` for help.');
}
}
+ throw new Exception('stdin has been closed');
}
_printUsage() {
- print('`c` or `continue`: to execute command');
+ print('`c` or `continue`: execute command');
print('`s` or `skip`: skip command');
- print('`b` or `break`: stop execution');
+ print('`d` or `disable`: disable stepper');
+ print('`b` or `break`: throw exception');
+ print('`q` or `quit`: terminate vm');
print('`h` or `help`: display this message');
}
}
+
+/// Converts a Stream<List<int> | int> to Stream<String> that fires an event
+/// for every line of data in the original Stream.
+class LineReader {
+ static const CR = 13;
+ static const LF = 10;
+
+ bool _crPrevious = false;
+ final _bytes = <int>[];
+ final _controller = new StreamController<String>.broadcast();
+
+ final Encoding encoding;
+
+ /// Only encodings that are a superset of ASCII are supported
+ /// TODO(DrMarcII): Support arbitrary encodings
+ LineReader(Stream /* <List<int> | int> */ stream,
+ {this.encoding: SYSTEM_ENCODING}) {
+ if (stream is Stdin) {
+ stdin.lineMode = false;
+ }
+ stream.listen(_listen,
+ onDone: _controller.close, onError: _controller.addError);
+ }
+
+ void _listen(/* List<int> | int */ data) {
+ if (data is List) {
+ data.forEach(_addByte);
+ } else {
+ _addByte(data);
+ }
+ }
+
+ void _addByte(int byte) {
+ if (_crPrevious && byte == LF) {
+ _crPrevious = false;
+ return;
+ }
+ _crPrevious = byte == CR;
+ if (byte == CR || byte == LF) {
+ _controller.add(encoding.decode(_bytes));
+ _bytes.clear();
+ } else {
+ _bytes.add(byte);
+ }
+ }
+
+ /// A Stream that fires for each line of data.
+ Stream<String> get onLine => _controller.stream;
+}