blob: 37ddee509aea7ed558ada4d3877bdec5d18d4c2f [file] [log] [blame]
// Copyright (c) 2012, 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.
// @dart = 2.9
// VMOptions=--no-enable-fast-object-copy
// VMOptions=--enable-fast-object-copy
// Dart test program for testing serialization of messages.
// VMOptions=--enable_type_checks --enable_asserts
library MessageTest;
import 'dart:async';
import 'dart:collection';
import 'dart:io';
import 'dart:isolate';
import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';
import 'dart:typed_data';
void echoMain(msg) {
SendPort replyTo = msg[0];
SendPort pong = msg[1];
ReceivePort port = new ReceivePort();
replyTo.send(port.sendPort);
port.listen((msg) {
if (msg == "halt") {
port.close();
} else {
pong.send(msg);
}
});
}
class A {
var field = 499;
A();
A.named(this.field);
}
class B extends A {
final field2;
B() : field2 = 99;
B.named(this.field2, x) : super.named(x);
}
class C extends B {
var field = 33;
get superField => super.field;
get superField2 => super.field2;
}
class M {
get field2 => 11;
}
class D extends C with M {
var gee = 123;
}
class E {
// Make sure E.fun is not removed by the tree shaker, as this test verifies
// that an object with a tear-off in E.fun cannot be sent.
@pragma("vm:entry-point")
Function fun;
E(this.fun);
static fooFun() => 499;
instanceFun() => 1234;
}
barFun() => 42;
class F {
final field = "field";
const F();
}
class G {
final field;
const G(this.field);
}
class Value {
final val;
Value(this.val);
operator ==(other) {
if (other is! Value) return false;
return other.val == val;
}
get hashCode => val;
}
void runTests(SendPort ping, Queue checks) {
ping.send("abc");
checks.add((x) => Expect.equals("abc", x));
ping.send([1, 2]);
checks.add((x) {
Expect.isTrue(x is List);
Expect.listEquals([1, 2], x);
// Make sure the list is mutable.
x[0] = 0;
Expect.equals(0, x[0]);
// List must be extendable.
x.add(3);
Expect.equals(3, x[2]);
});
List fixed = new List(2);
fixed[0] = 0;
fixed[1] = 1;
ping.send(fixed);
checks.add((x) {
Expect.isTrue(x is List);
Expect.listEquals([0, 1], x);
// List must be mutable.
x[0] = 3;
Expect.equals(3, x[0]);
// List must be fixed length.
Expect.throws(() {
x.add(5);
});
});
List cyclic = [];
cyclic.add(cyclic);
ping.send(cyclic);
checks.add((x) {
Expect.isTrue(x is List);
Expect.equals(1, x.length);
Expect.identical(x, x[0]);
// List must be mutable.
x[0] = 55;
Expect.equals(55, x[0]);
// List must be extendable.
x.add(42);
Expect.equals(42, x[1]);
});
List cyclic2 = new List(1);
cyclic2[0] = cyclic2;
ping.send(cyclic2);
checks.add((x) {
Expect.isTrue(x is List);
Expect.equals(1, x.length);
Expect.identical(x, x[0]);
// List must be mutable.
x[0] = 55;
Expect.equals(55, x[0]);
// List must be fixed.
Expect.throws(() => x.add(42));
});
List constList = const [1, 2];
ping.send(constList);
checks.add((x) {
Expect.isTrue(x is List);
Expect.listEquals([1, 2], x);
// Make sure the list is immutable.
Expect.throws(() => x[0] = 0); // //# constList: ok
// List must not be extendable.
Expect.throws(() => x.add(3));
Expect.identical(x, constList); // //# constList_identical: ok
});
Uint8List uint8 = new Uint8List(2);
uint8[0] = 0;
uint8[1] = 1;
ping.send(uint8);
checks.add((x) {
Expect.isTrue(x is Uint8List);
Expect.equals(2, x.length);
Expect.equals(0, x[0]);
Expect.equals(1, x[1]);
});
Uint16List uint16 = new Uint16List(2);
uint16[0] = 0;
uint16[1] = 1;
ByteBuffer byteBuffer = uint16.buffer;
ping.send(byteBuffer); // //# byteBuffer: ok
checks.add( // //# byteBuffer: ok
(x) {
Expect.isTrue(x is ByteBuffer);
Uint16List uint16View = new Uint16List.view(x);
Expect.equals(2, uint16View.length);
Expect.equals(0, uint16View[0]);
Expect.equals(1, uint16View[1]);
}
) // //# byteBuffer: ok
;
Int32x4List list32x4 = new Int32x4List(2);
list32x4[0] = new Int32x4(1, 2, 3, 4);
list32x4[1] = new Int32x4(5, 6, 7, 8);
ping.send(list32x4);
checks.add(
(x) {
Expect.isTrue(x is Int32x4List);
Expect.equals(2, x.length);
Int32x4 entry1 = x[0];
Int32x4 entry2 = x[1];
Expect.equals(1, entry1.x);
Expect.equals(2, entry1.y);
Expect.equals(3, entry1.z);
Expect.equals(4, entry1.w);
Expect.equals(5, entry2.x);
Expect.equals(6, entry2.y);
Expect.equals(7, entry2.z);
Expect.equals(8, entry2.w);
});
Float32x4List flist32x4 = new Float32x4List(2);
flist32x4[0] = new Float32x4(1.5, 2.5, 3.5, 4.5);
flist32x4[1] = new Float32x4(5.5, 6.5, 7.5, 8.5);
ping.send(flist32x4);
checks.add(
(x) {
Expect.isTrue(x is Float32x4List);
Expect.equals(2, x.length);
Float32x4 entry1 = x[0];
Float32x4 entry2 = x[1];
Expect.equals(1.5, entry1.x);
Expect.equals(2.5, entry1.y);
Expect.equals(3.5, entry1.z);
Expect.equals(4.5, entry1.w);
Expect.equals(5.5, entry2.x);
Expect.equals(6.5, entry2.y);
Expect.equals(7.5, entry2.z);
Expect.equals(8.5, entry2.w);
});
Float64x2List flist64x2 = new Float64x2List(2);
flist64x2[0] = new Float64x2(1.5, 2.5);
flist64x2[1] = new Float64x2(5.5, 6.5);
ping.send(flist64x2);
checks.add(
(x) {
Expect.isTrue(x is Float64x2List);
Expect.equals(2, x.length);
Float64x2 entry1 = x[0];
Float64x2 entry2 = x[1];
Expect.equals(1.5, entry1.x);
Expect.equals(2.5, entry1.y);
Expect.equals(5.5, entry2.x);
Expect.equals(6.5, entry2.y);
});
ping.send({"foo": 499, "bar": 32});
checks.add((x) {
Expect.isTrue(x is LinkedHashMap);
Expect.listEquals(["foo", "bar"], x.keys.toList());
Expect.listEquals([499, 32], x.values.toList());
Expect.equals(499, x["foo"]);
Expect.equals(32, x["bar"]);
// Must be mutable.
x["foo"] = 22;
Expect.equals(22, x["foo"]);
// Must be extendable.
x["gee"] = 499;
Expect.equals(499, x["gee"]);
});
Map<String, int> mapWithRemovedKey = {"foo": 499, "bar": 32};
mapWithRemovedKey.remove("foo");
ping.send(mapWithRemovedKey);
checks.add((x) {
Expect.isTrue(x is LinkedHashMap);
Expect.listEquals(["bar"], x.keys.toList());
Expect.listEquals([32], x.values.toList());
Expect.equals(32, x["bar"]);
});
// Test map where a key does not define ==/hashCode.
Map<A, int> mapWithIdentityKey = new Map<A, int>();
mapWithIdentityKey[new A()] = 499;
ping.send(mapWithIdentityKey);
checks.add((x) {
Expect.isTrue(x is LinkedHashMap);
int value = x.values.first;
Expect.equals(499, value);
A key = x.keys.first;
Expect.equals(499, x[key]);
});
ping.send({0: 499, 1: 32});
checks.add((x) {
Expect.isTrue(x is LinkedHashMap);
Expect.listEquals([0, 1], x.keys.toList());
Expect.listEquals([499, 32], x.values.toList());
// Must be mutable.
x[0] = 22;
Expect.equals(22, x[0]);
// Must be extendable.
x[123] = 499;
Expect.equals(499, x[123]);
});
Map cyclicMap = {};
cyclicMap["cycle"] = cyclicMap;
ping.send(cyclicMap);
checks.add((x) {
Expect.isTrue(x is LinkedHashMap);
Expect.identical(x, x["cycle"]);
// Must be mutable.
x["cycle"] = 22;
Expect.equals(22, x["cycle"]);
// Must be extendable.
x["gee"] = 499;
Expect.equals(499, x["gee"]);
});
Map constMap = const {'foo': 499};
ping.send(constMap);
checks.add((x) {
Expect.isTrue(x is Map);
print(x.length);
Expect.equals(1, x.length);
Expect.equals(499, x['foo']);
Expect.identical(constMap, x); // //# constMap: ok
Expect.throws(() => constMap['bar'] = 42);
});
ping.send(new A());
checks.add((x) {
Expect.isTrue(x is A);
Expect.equals(499, x.field);
});
ping.send(new A.named(42));
checks.add((x) {
Expect.isTrue(x is A);
Expect.equals(42, x.field);
});
ping.send(new B());
checks.add((x) {
Expect.isTrue(x is A);
Expect.isTrue(x is B);
Expect.equals(499, x.field);
Expect.equals(99, x.field2);
Expect.throws(() => x.field2 = 22);
});
ping.send(new B.named(1, 2));
checks.add((x) {
Expect.isTrue(x is A);
Expect.isTrue(x is B);
Expect.equals(2, x.field);
Expect.equals(1, x.field2);
Expect.throws(() => x.field2 = 22);
});
ping.send(new C());
checks.add((x) {
Expect.isTrue(x is A);
Expect.isTrue(x is B);
Expect.isTrue(x is C);
Expect.equals(33, x.field);
Expect.equals(99, x.field2);
Expect.equals(499, x.superField);
Expect.throws(() => x.field2 = 22);
});
ping.send(new D());
checks.add((x) {
Expect.isTrue(x is A);
Expect.isTrue(x is B);
Expect.isTrue(x is C);
Expect.isTrue(x is D);
Expect.isTrue(x is M);
Expect.equals(33, x.field);
Expect.equals(11, x.field2);
Expect.equals(499, x.superField);
Expect.equals(99, x.superField2);
Expect.throws(() => x.field2 = 22);
});
D cyclicD = new D();
ping.send(cyclicD);
checks.add((x) {
Expect.isTrue(x is A);
Expect.isTrue(x is B);
Expect.isTrue(x is C);
Expect.isTrue(x is D);
Expect.isTrue(x is M);
Expect.equals(11, x.field2);
Expect.equals(499, x.superField);
Expect.equals(99, x.superField2);
Expect.throws(() => x.field2 = 22);
});
ping.send(new E(E.fooFun)); // //# fun: ok
checks.add((x) { // //# fun: continued
Expect.equals(E.fooFun, x.fun); // //# fun: continued
Expect.equals(499, x.fun()); // //# fun: continued
}); // //# fun: continued
ping.send(new E(barFun)); // //# fun: continued
checks.add((x) { // //# fun: continued
Expect.equals(barFun, x.fun); // //# fun: continued
Expect.equals(42, x.fun()); // //# fun: continued
}); // //# fun: continued
ping.send(new E(new E(null).instanceFun));
checks.add((x) {
Expect.equals(1234, (x as E).fun());
});
F nonConstF = new F();
ping.send(nonConstF);
checks.add((x) {
Expect.equals("field", x.field);
Expect.isFalse(identical(nonConstF, x));
});
const F constF = const F();
ping.send(constF);
checks.add((x) {
Expect.equals("field", x.field);
Expect.identical(constF, x); // //# constInstance: ok
});
G g1 = new G(nonConstF);
G g2 = new G(constF);
G g3 = const G(constF);
ping.send(g1);
ping.send(g2);
ping.send(g3);
checks.add((x) {
// g1.
Expect.isTrue(x is G);
Expect.isFalse(identical(g1, x));
F f = x.field;
Expect.equals("field", f.field);
Expect.isFalse(identical(nonConstF, f));
});
checks.add((x) {
// g2.
Expect.isTrue(x is G);
Expect.isFalse(identical(g1, x));
F f = x.field;
Expect.equals("field", f.field);
Expect.identical(constF, f); // //# constInstance: continued
});
checks.add((x) {
// g3.
Expect.isTrue(x is G);
Expect.identical(g3, x); // //# constInstance: continued
F f = x.field;
Expect.equals("field", f.field);
Expect.identical(constF, f); // //# constInstance: continued
});
// Make sure objects in a map are serialized and deserialized in the correct
// order.
Map m = new Map();
Value val1 = new Value(1);
Value val2 = new Value(2);
m[val1] = val2;
m[val2] = val1;
// Possible bug we want to catch:
// serializer runs through keys first, and then the values:
// - id1 = val1, id2 = val2, ref[id2], ref[id1]
// deserializer runs through the keys and values in order:
// - val1; // id1.
// - ref[id2]; // boom. Wasn't deserialized yet.
ping.send(m);
checks.add((x) {
Expect.isTrue(x is Map);
Expect.equals(2, x.length);
Expect.equals(val2, x[val1]);
Expect.equals(val1, x[val2]);
Expect.identical(x.keys.elementAt(0), x.values.elementAt(1));
Expect.identical(x.keys.elementAt(1), x.values.elementAt(0));
});
}
void main() {
asyncStart();
Queue checks = new Queue();
ReceivePort testPort = new ReceivePort();
Completer completer = new Completer();
testPort.listen((msg) {
Function check = checks.removeFirst();
check(msg);
if (checks.isEmpty) {
completer.complete();
testPort.close();
}
});
ReceivePort initialReplyPort = new ReceivePort();
Isolate
.spawn(echoMain, [initialReplyPort.sendPort, testPort.sendPort])
.then((_) => initialReplyPort.first)
.then((_ping) {
SendPort ping = _ping;
runTests(ping, checks);
Expect.isTrue(checks.length > 0);
completer.future.then((_) => ping.send("halt")).then((_) => asyncEnd());
});
}