|  | // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import 'dart:convert'; | 
|  |  | 
|  | import 'package:expect/expect.dart'; | 
|  |  | 
|  | // This test implements a new special interface that can be used to | 
|  | // send data more efficiently between two converters. | 
|  |  | 
|  | abstract class MyChunkedIntSink implements ChunkedConversionSink<int> { | 
|  | MyChunkedIntSink(); | 
|  | factory MyChunkedIntSink.from(sink) = IntAdapterSink; | 
|  | factory MyChunkedIntSink.withCallback(callback) { | 
|  | var sink = new ChunkedConversionSink.withCallback(callback); | 
|  | return new MyChunkedIntSink.from(sink); | 
|  | } | 
|  |  | 
|  | add(int i); | 
|  | close(); | 
|  |  | 
|  | // The special method. | 
|  | void specialI(int i); | 
|  | } | 
|  |  | 
|  | class IntAdapterSink extends MyChunkedIntSink { | 
|  | final _sink; | 
|  | IntAdapterSink(this._sink); | 
|  | add(o) => _sink.add(o); | 
|  | close() => _sink.close(); | 
|  | void specialI(int o) => add(o); | 
|  | } | 
|  |  | 
|  | abstract class MyChunkedBoolSink implements ChunkedConversionSink<bool> { | 
|  | MyChunkedBoolSink(); | 
|  | factory MyChunkedBoolSink.from(sink) = BoolAdapterSink; | 
|  | factory MyChunkedBoolSink.withCallback(callback) { | 
|  | var sink = new ChunkedConversionSink.withCallback(callback); | 
|  | return new MyChunkedBoolSink.from(sink); | 
|  | } | 
|  |  | 
|  | add(bool b); | 
|  | close(); | 
|  |  | 
|  | specialB(bool b); | 
|  | } | 
|  |  | 
|  | class BoolAdapterSink extends MyChunkedBoolSink { | 
|  | final _sink; | 
|  | BoolAdapterSink(this._sink); | 
|  | add(o) => _sink.add(o); | 
|  | close() => _sink.close(); | 
|  | specialB(o) => add(o); | 
|  | } | 
|  |  | 
|  | class IntBoolConverter1 extends Converter<int, bool> { | 
|  | bool convert(int input) => input > 0; | 
|  |  | 
|  | startChunkedConversion(sink) { | 
|  | if (sink is! MyChunkedBoolSink) sink = new MyChunkedBoolSink.from(sink); | 
|  | return new IntBoolConverter1Sink(sink); | 
|  | } | 
|  | } | 
|  |  | 
|  | class BoolIntConverter1 extends Converter<bool, int> { | 
|  | int convert(bool input) => input ? 1 : 0; | 
|  |  | 
|  | startChunkedConversion(sink) { | 
|  | if (sink is! MyChunkedIntSink) sink = new MyChunkedIntSink.from(sink); | 
|  | return new BoolIntConverter1Sink(sink); | 
|  | } | 
|  | } | 
|  |  | 
|  | int specialICounter = 0; | 
|  | int specialBCounter = 0; | 
|  |  | 
|  | class IntBoolConverter1Sink extends MyChunkedIntSink { | 
|  | var outSink; | 
|  | IntBoolConverter1Sink(this.outSink); | 
|  |  | 
|  | add(int i) { | 
|  | outSink.specialB(i > 0); | 
|  | } | 
|  |  | 
|  | void specialI(int i) { | 
|  | specialICounter++; | 
|  | add(i); | 
|  | } | 
|  |  | 
|  | close() => outSink.close(); | 
|  | } | 
|  |  | 
|  | class BoolIntConverter1Sink extends MyChunkedBoolSink { | 
|  | var outSink; | 
|  | BoolIntConverter1Sink(this.outSink); | 
|  |  | 
|  | add(bool b) { | 
|  | outSink.specialI(b ? 1 : 0); | 
|  | } | 
|  |  | 
|  | specialB(bool b) { | 
|  | specialBCounter++; | 
|  | add(b); | 
|  | } | 
|  |  | 
|  | close() => outSink.close(); | 
|  | } | 
|  |  | 
|  | class IdentityConverter<T> extends Converter<T, T> { | 
|  | T convert(T x) => x; | 
|  |  | 
|  | startChunkedConversion(sink) { | 
|  | return new IdentitySink<T>(sink); | 
|  | } | 
|  | } | 
|  |  | 
|  | class IdentitySink<T> implements ChunkedConversionSink<T> { | 
|  | final _sink; | 
|  | IdentitySink(this._sink); | 
|  | void add(T o) => _sink.add(o); | 
|  | close() => _sink.close(); | 
|  | } | 
|  |  | 
|  | main() { | 
|  | var intSink, boolSink, sink, sink2; | 
|  |  | 
|  | // Test int->bool converter individually. | 
|  | Converter<int, bool> int2boolConverter = new IntBoolConverter1(); | 
|  | Expect.listEquals( | 
|  | [true, false, true], [2, -2, 2].map(int2boolConverter.convert).toList()); | 
|  | var hasExecuted = false; | 
|  | boolSink = new MyChunkedBoolSink.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([true, false, true], value); | 
|  | }); | 
|  | intSink = int2boolConverter.startChunkedConversion(boolSink); | 
|  | intSink.add(3); | 
|  | intSink.specialI(-3); | 
|  | intSink.add(3); | 
|  | intSink.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | Expect.equals(1, specialICounter); | 
|  | specialICounter = 0; | 
|  | hasExecuted = false; | 
|  |  | 
|  | // Test bool->int converter individually. | 
|  | Converter<bool, int> bool2intConverter = new BoolIntConverter1(); | 
|  | Expect.listEquals( | 
|  | [1, 0, 1], [true, false, true].map(bool2intConverter.convert).toList()); | 
|  | hasExecuted = false; | 
|  | intSink = new MyChunkedIntSink.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([1, 0, 1], value); | 
|  | }); | 
|  | boolSink = bool2intConverter.startChunkedConversion(intSink); | 
|  | boolSink.specialB(true); | 
|  | boolSink.add(false); | 
|  | boolSink.add(true); | 
|  | boolSink.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | Expect.equals(1, specialBCounter); | 
|  | specialBCounter = 0; | 
|  | hasExecuted = false; | 
|  |  | 
|  | // Test identity converter individually. | 
|  | var identityConverter = new IdentityConverter(); | 
|  | hasExecuted = false; | 
|  | sink = new ChunkedConversionSink.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([1, 2, 3], value); | 
|  | }); | 
|  | sink2 = identityConverter.startChunkedConversion(sink); | 
|  | [1, 2, 3].forEach(sink2.add); | 
|  | sink2.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | hasExecuted = false; | 
|  |  | 
|  | // Test fused converters. | 
|  | Converter<int, int> fused = int2boolConverter.fuse(bool2intConverter); | 
|  | Expect.listEquals([1, 0, 1], [2, -2, 2].map(fused.convert).toList()); | 
|  | hasExecuted = false; | 
|  | Sink<int> intSink2 = new MyChunkedIntSink.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([1, 0, 1], value); | 
|  | }); | 
|  | intSink = fused.startChunkedConversion(intSink2); | 
|  | intSink.specialI(3); | 
|  | intSink.add(-3); | 
|  | intSink.add(3); | 
|  | intSink.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | Expect.equals(3, specialBCounter); | 
|  | specialBCounter = 0; | 
|  | Expect.equals(1, specialICounter); | 
|  | specialICounter = 0; | 
|  | hasExecuted = false; | 
|  |  | 
|  | // With identity in front. | 
|  | Converter<int, int> fused2 = new IdentityConverter<int>().fuse(fused); | 
|  | hasExecuted = false; | 
|  | intSink2 = new MyChunkedIntSink.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([1, 0, 1], value); | 
|  | }); | 
|  | sink = fused2.startChunkedConversion(intSink2); | 
|  | Expect.isFalse(sink is MyChunkedIntSink); | 
|  | sink.add(3); | 
|  | sink.add(-3); | 
|  | sink.add(3); | 
|  | sink.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | Expect.equals(3, specialBCounter); | 
|  | specialBCounter = 0; | 
|  | Expect.equals(0, specialICounter); | 
|  | specialICounter = 0; | 
|  | hasExecuted = false; | 
|  |  | 
|  | // With identity at the end. | 
|  | fused2 = fused.fuse(new IdentityConverter()); | 
|  | hasExecuted = false; | 
|  | sink = new ChunkedConversionSink<int>.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([1, 0, 1], value); | 
|  | }); | 
|  | intSink = fused2.startChunkedConversion(sink); | 
|  | Expect.isTrue(intSink is MyChunkedIntSink); | 
|  | intSink.specialI(3); | 
|  | intSink.add(-3); | 
|  | intSink.specialI(3); | 
|  | intSink.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | Expect.equals(3, specialBCounter); | 
|  | specialBCounter = 0; | 
|  | Expect.equals(2, specialICounter); | 
|  | specialICounter = 0; | 
|  | hasExecuted = false; | 
|  |  | 
|  | // With identity between the two converters. | 
|  | fused = | 
|  | int2boolConverter.fuse(new IdentityConverter()).fuse(bool2intConverter); | 
|  | Expect.listEquals([1, 0, 1], [2, -2, 2].map(fused.convert).toList()); | 
|  | hasExecuted = false; | 
|  | intSink2 = new MyChunkedIntSink.withCallback((value) { | 
|  | hasExecuted = true; | 
|  | Expect.listEquals([1, 0, 1], value); | 
|  | }); | 
|  | intSink = fused.startChunkedConversion(intSink2); | 
|  | intSink.specialI(3); | 
|  | intSink.add(-3); | 
|  | intSink.add(3); | 
|  | intSink.close(); | 
|  | Expect.isTrue(hasExecuted); | 
|  | Expect.equals(0, specialBCounter); | 
|  | specialBCounter = 0; | 
|  | Expect.equals(1, specialICounter); | 
|  | specialICounter = 0; | 
|  | hasExecuted = false; | 
|  | } |