test: i hate this

This commit is contained in:
Orion Kindel 2023-04-13 16:16:40 -05:00
parent c700a6c43c
commit f00dd85502
Signed by untrusted user who does not match committer: orion
GPG Key ID: 6D4165AE4C928719
10 changed files with 516 additions and 37 deletions

View File

@ -63,9 +63,6 @@ lazy val root = project
},
cargoBuild := {
println(Seq("sh", "-c", "cd glue; cargo rustc -- -Awarnings") !!)
// println(
// Seq("sh", "-c", "cd glue; RUST_BACKTRACE=full cargo test --quiet --features e2e") !!
// ) // very important: test suite validates interfaces
},
fullBuild := {
cargoBuild.value

8
glue/Cargo.lock generated
View File

@ -580,11 +580,15 @@ dependencies = [
[[package]]
name = "toad-jni"
version = "0.8.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0bb1a31e7259c1338191283a47f7cb8ae7f51eb62455fd7e123d2be304f1894"
checksum = "ef61b22c3478d481635b9eb67496d4dc18f22b8321961aca5dc2084f5253ccb7"
dependencies = [
"jni",
"nb",
"no-std-net",
"tinyvec",
"toad",
"toad-array 0.5.0",
"toad-len",
"toad-stem",

View File

@ -15,7 +15,7 @@ e2e = []
jni = "0.21.1"
nb = "1"
toad = "0.17.3"
toad-jni = "0.8.0"
toad-jni = "0.9.1"
no-std-net = "0.6"
toad-msg = "0.18.1"
tinyvec = {version = "1.5", default_features = false, features = ["rustc_1_55"]}

View File

@ -6,8 +6,27 @@ use jni::JavaVM;
use mem::SharedMemoryRegion;
mod runtime {
use std::collections::BTreeMap;
use toad::platform::Effect;
use toad::std::{dtls, Platform};
use toad::step::runtime::std::Runtime as DefaultSteps;
use toad_jni::java::nio::channels::PeekableDatagramChannel;
use toad_msg::{OptValue, OptNumber};
#[derive(Clone, Copy, Debug)]
pub struct PlatformTypes;
impl toad::platform::PlatformTypes for PlatformTypes {
type MessagePayload = Vec<u8>;
type MessageOptionBytes = Vec<u8>;
type MessageOptionMapOptionValues = Vec<OptValue<Vec<u8>>>;
type MessageOptions = BTreeMap<OptNumber, Vec<OptValue<Vec<u8>>>>;
type Clock = toad::std::Clock;
type Socket = PeekableDatagramChannel;
type Effects = Vec<Effect<Self>>;
}
pub type Runtime = Platform<dtls::N, DefaultSteps<dtls::N>>;
}

View File

@ -1,12 +1,16 @@
package dev.toad;
import dev.toad.ffi.*;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.DatagramChannel;
import java.time.Duration;
import java.util.Optional;
import java.util.function.Function;
public final class Toad {
public final class Toad implements AutoCloseable {
static native Config defaultConfigImpl();
@ -26,34 +30,36 @@ public final class Toad {
final Ptr ptr;
final Config config;
final DatagramChannel channel;
static native long init(Config o);
static native Optional<dev.toad.msg.ref.Message> pollReq(long ptr);
native Optional<dev.toad.msg.ref.Message> pollReq(long ptr);
static native Optional<dev.toad.msg.ref.Message> pollResp(
native Optional<dev.toad.msg.ref.Message> pollResp(
long ptr,
dev.toad.msg.Token t,
InetSocketAddress n
);
Optional<dev.toad.msg.ref.Message> pollReq() {
return Toad.pollReq(this.ptr.addr());
return this.pollReq(this.ptr.addr());
}
Optional<dev.toad.msg.ref.Message> pollResp(
dev.toad.msg.Token regarding,
InetSocketAddress from
) {
return Toad.pollResp(this.ptr.addr(), regarding, from);
return this.pollResp(this.ptr.addr(), regarding, from);
}
public static BuilderRequiresBindToAddress builder() {
public static BuilderRequiresSocket builder() {
return new Builder();
}
Toad(Config o) {
Toad(Config o, DatagramChannel channel) {
this.config = o;
this.channel = channel;
this.ptr = Ptr.register(this.getClass(), this.init(o));
}
@ -61,22 +67,33 @@ public final class Toad {
return this.config;
}
public interface BuilderRequiresBindToAddress {
Toad.Builder port(short port);
Toad.Builder address(InetSocketAddress addr);
@Override
public void close() {
this.ptr.release();
}
public static final class Builder implements BuilderRequiresBindToAddress {
public interface BuilderRequiresSocket {
Toad.Builder port(short port);
Toad.Builder address(InetSocketAddress addr);
Toad.Builder channel(DatagramChannel channel);
}
public static final class Builder implements BuilderRequiresSocket {
Optional<IOException> ioException = Optional.empty();
Config.Msg.Builder msg = Config.Msg.builder();
Optional<InetSocketAddress> addr = Optional.empty();
Optional<DatagramChannel> channel = Optional.empty();
u8 concurrency = Toad.defaultConfig().concurrency;
Builder() {}
public Toad build() {
var cfg = new Config(this.addr.get(), this.concurrency, this.msg.build());
return new Toad(cfg);
public Toad build() throws IOException {
if (!this.ioException.isEmpty()) {
var cfg = new Config(this.concurrency, this.msg.build());
return new Toad(cfg, this.channel.get());
} else {
throw this.ioException.get();
}
}
public Builder msg(Function<Config.Msg.Builder, Config.Msg.Builder> f) {
@ -89,7 +106,20 @@ public final class Toad {
}
public Builder address(InetSocketAddress addr) {
this.addr = Optional.of(addr);
try {
DatagramChannel channel = DatagramChannel.open(
java.net.StandardProtocolFamily.INET
);
channel.bind(addr);
return this.channel(channel);
} catch (java.io.IOException e) {
this.ioException = Optional.of(e);
return this;
}
}
public Builder channel(DatagramChannel channel) {
this.channel = Optional.of(channel);
return this;
}
@ -101,12 +131,10 @@ public final class Toad {
public static final class Config {
final InetSocketAddress addr;
final u8 concurrency;
final Msg msg;
Config(InetSocketAddress addr, u8 concurrency, Msg msg) {
this.addr = addr;
Config(u8 concurrency, Msg msg) {
this.concurrency = concurrency;
this.msg = msg;
}
@ -114,9 +142,7 @@ public final class Toad {
@Override
public boolean equals(Object other) {
return switch (other) {
case Config o -> o.addr == this.addr &&
o.concurrency == this.concurrency &&
o.msg == this.msg;
case Config o -> o.concurrency == this.concurrency && o.msg == this.msg;
default -> false;
};
}

423
src/test/java/Mock.java Normal file
View File

@ -0,0 +1,423 @@
package mock.java.nio.channels;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ProtocolFamily;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.MembershipKey;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.AbstractSelectionKey;
import java.nio.channels.spi.AbstractSelector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Set;
public class Mock {
public static class DatagramSocket extends java.net.DatagramSocket {
public InetSocketAddress address;
public DatagramSocket(int port)
throws SocketException, UnknownHostException {
var addr = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
this.address = new InetSocketAddress(addr, port);
}
public DatagramSocket(int port, InetAddress addr) throws SocketException {
this.address = new InetSocketAddress(addr, port);
}
public InetSocketAddress address() {
return this.address;
}
}
public static class Channel
extends DatagramChannel
implements
WritableByteChannel,
GatheringByteChannel,
ScatteringByteChannel,
ReadableByteChannel {
public Map<SocketAddress, List<ByteBuffer>> sent = new HashMap<>();
public Map<SocketAddress, List<ByteBuffer>> recv = new HashMap<>();
public List<Byte> bytes = new ArrayList<>();
public DatagramSocket sock;
public Channel() throws SocketException, UnknownHostException {
this(new DatagramSocket(1234));
}
public Channel(DatagramSocket sock)
throws SocketException, UnknownHostException {
super(new SelectorProvider());
this.sock = sock;
}
@Override
public int send(ByteBuffer src, SocketAddress target) {
var sent = this.sent.get(target);
if (sent == null) {
var list = new ArrayList<ByteBuffer>();
this.sent.put(target, list);
}
this.sent.get(target).add(src);
return (int) src.capacity();
}
@Override
public SocketAddress receive(ByteBuffer dst) {
for (Map.Entry<SocketAddress, List<ByteBuffer>> ent : this.recv.entrySet()) {
if (ent.getValue().size() == 0) {
this.recv.remove(ent.getKey());
} else {
var buf = ent.getValue().remove(0);
dst.put(buf);
return ent.getKey();
}
}
return null;
}
@Override
public int write(ByteBuffer src) {
src.rewind();
for (int j = 0; j < src.capacity(); j++) {
this.bytes.add(src.get(j));
}
return (int) src.capacity();
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) {
long writ = 0;
for (ByteBuffer buf : srcs) {
writ += this.write(buf);
}
return writ;
}
public int read(ByteBuffer dst, int start) {
int orig = (int) dst.position();
for (Byte b : this.bytes.subList(start, this.bytes.size())) {
dst.put(b);
}
return (int) dst.position() - orig;
}
@Override
public int read(ByteBuffer dst) {
return this.read(dst, 0);
}
@Override
public long read(ByteBuffer[] dsts, int off, int len) {
long n = 0;
for (ByteBuffer buf : dsts) {
n += this.read(buf, (int) n);
}
return n;
}
@Override
public void implConfigureBlocking(boolean b) {}
@Override
public void implCloseSelectableChannel() {}
@Override
public SocketAddress getLocalAddress() {
return this.sock.address();
}
@Override
public SocketAddress getRemoteAddress() {
return null;
}
@Override
public DatagramChannel disconnect() {
return this;
}
@Override
public DatagramChannel bind(SocketAddress local) {
return this;
}
@Override
public DatagramChannel connect(SocketAddress remote) {
return this;
}
@Override
public boolean isConnected() {
return false;
}
@Override
public DatagramSocket socket() {
return this.sock;
}
@Override
public MembershipKey join(InetAddress group, NetworkInterface interf) {
throw new Error("unimplemented");
}
@Override
public MembershipKey join(
InetAddress group,
NetworkInterface interf,
InetAddress source
) {
throw new Error("unimplemented");
}
@Override
public <T> DatagramChannel setOption(SocketOption<T> name, T value) {
return this;
}
@Override
public <T> T getOption(SocketOption<T> name) {
throw new Error("unimplemented");
}
@Override
public Set<SocketOption<?>> supportedOptions() {
throw new Error("unimplemented");
}
}
public static class Pipe extends java.nio.channels.Pipe {
public SinkChannel sink;
public SourceChannel source;
public Pipe() throws SocketException, UnknownHostException {
this.sink = new SinkChannel();
this.source = new SourceChannel();
}
@Override
public SinkChannel sink() {
return this.sink;
}
@Override
public SourceChannel source() {
return this.source;
}
public static class SinkChannel
extends java.nio.channels.Pipe.SinkChannel
implements WritableByteChannel, GatheringByteChannel {
public SinkChannel() throws SocketException, UnknownHostException {
super(new SelectorProvider());
this.channel = new Channel();
}
public Channel channel;
@Override
public void implCloseSelectableChannel() {}
@Override
public void implConfigureBlocking(boolean block) {}
@Override
public int write(ByteBuffer src) {
return this.channel.write(src);
}
@Override
public long write(ByteBuffer[] srcs) throws IOException {
return this.channel.write(srcs);
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) {
return this.channel.write(srcs, offset, length);
}
}
public static class SourceChannel
extends java.nio.channels.Pipe.SourceChannel {
public Channel channel;
public SourceChannel() throws SocketException, UnknownHostException {
super(new SelectorProvider());
this.channel = new Channel();
}
@Override
public void implCloseSelectableChannel() {}
@Override
public void implConfigureBlocking(boolean block) {}
@Override
public int read(ByteBuffer buf) {
return this.channel.read(buf);
}
@Override
public long read(ByteBuffer[] dsts) throws IOException {
return this.channel.read(dsts);
}
@Override
public long read(ByteBuffer[] dsts, int off, int len) {
return this.channel.read(dsts, off, len);
}
}
}
public static class SelectionKey extends AbstractSelectionKey {
public SelectionKey() {}
@Override
public int readyOps() {
return 0;
}
@Override
public int interestOps() {
return 0;
}
@Override
public java.nio.channels.SelectionKey interestOps(int o) {
return this;
}
@Override
public java.nio.channels.Selector selector() {
return new Selector();
}
@Override
public java.nio.channels.SelectableChannel channel() {
try {
return new Channel();
} catch (UnknownHostException e) {
throw new Error(e);
} catch (SocketException e) {
throw new Error(e);
}
}
}
public static class Selector extends AbstractSelector {
public Selector() {
super(new SelectorProvider());
}
@Override
public void implCloseSelector() {}
@Override
public java.nio.channels.SelectionKey register(
AbstractSelectableChannel ch,
int ops,
Object att
) {
return new SelectionKey();
}
@Override
public java.nio.channels.Selector wakeup() {
return this;
}
@Override
public int select(long timeout) {
return 0;
}
@Override
public int select() {
return 0;
}
@Override
public int selectNow() {
return 0;
}
@Override
public Set<java.nio.channels.SelectionKey> selectedKeys() {
throw new Error("unimplemented");
}
@Override
public Set<java.nio.channels.SelectionKey> keys() {
throw new Error("unimplemented");
}
}
public static class SelectorProvider
extends java.nio.channels.spi.SelectorProvider {
@Override
public java.nio.channels.DatagramChannel openDatagramChannel() {
return this.openDatagramChannel(StandardProtocolFamily.INET);
}
@Override
public java.nio.channels.DatagramChannel openDatagramChannel(
ProtocolFamily proto
) {
throw new Error("unimplemented");
}
@Override
public SocketChannel openSocketChannel() {
throw new Error("unimplemented");
}
@Override
public ServerSocketChannel openServerSocketChannel() {
throw new Error("unimplemented");
}
@Override
public AbstractSelector openSelector() {
return new Selector();
}
@Override
public Pipe openPipe() throws SocketException, UnknownHostException {
return new Pipe();
}
}
}

View File

@ -0,0 +1,4 @@
class Client extends munit.FunSuite {
test("client gets") {
}
}

8
src/test/scala/E2E.scala Normal file
View File

@ -0,0 +1,8 @@
import dev.toad.*;
import mock.java.nio.channels.Mock;
class E2E extends munit.FunSuite {
test("foo") {
val mock = Mock.Channel();
}
}

View File

@ -0,0 +1,7 @@
import sys.process._
class Glue extends munit.FunSuite {
test("cargo test") {
Seq("sh", "-c", "cd glue; RUST_BACKTRACE=full cargo test --quiet --features e2e").!!
}
}

View File

@ -1,9 +0,0 @@
// For more information on writing tests, see
// https://scalameta.org/munit/docs/getting-started.html
class MySuite extends munit.FunSuite {
test("example test that succeeds") {
val obtained = 42
val expected = 42
assertEquals(obtained, expected)
}
}