Livt packages

Livt.Net

Version 1.4.1

Ethernet, IPv4, TCP, UDP, ARP, TFTP, and Telnet networking components for Livt hardware.

Published files (163)

tests/FrameLinkTest.lvt

namespace Livt.Net.Tests
using Livt.Net

/**
 * Checks RX/TX ownership, backpressure, cancellation and coordinated reset through capability-bound consumers.
 */
@Test
component FrameLinkTest
{
	rx: TestFrameReceiver<64>
	rxChecks: ReceiveContractChecks<TestFrameReceiver<64>>
	region: PacketRegion<TestFrameReceiver<64>>
	data: ArrayPacketData<65>
	tx: TestFrameTransmitter<ArrayPacketData<65>, 64>
	checks: TransmitContractChecks<TestFrameTransmitter<ArrayPacketData<65>, 64>>
	faulty: FaultingFrameData
	fixedTx: TestFrameTransmitter<FaultingFrameData, 64, false>
	fixedChecks: TransmitContractChecks<TestFrameTransmitter<FaultingFrameData, 64, false>>
	forwardRx: TestFrameReceiver<64>
	forwardTx: TestFrameTransmitter<TestFrameReceiver<64>, 64>
	service: FrameForwarder<TestFrameReceiver<64>, TestFrameTransmitter<TestFrameReceiver<64>, 64>>

	/**
	 * Wires simulated links, capability-bound assertions and a forwarder in the same clock context.
	 */
	new()
	{
		rx = new TestFrameReceiver<64>()
		rxChecks = new ReceiveContractChecks<TestFrameReceiver<64>>(rx)
		region = new PacketRegion<TestFrameReceiver<64>>(rx)
		data = new ArrayPacketData<65>()
		tx = new TestFrameTransmitter<ArrayPacketData<65>, 64>(data)
		checks = new TransmitContractChecks<TestFrameTransmitter<ArrayPacketData<65>, 64>>(tx)
		faulty = new FaultingFrameData()
		fixedTx = new TestFrameTransmitter<FaultingFrameData, 64, false>(faulty)
		fixedChecks = new TransmitContractChecks<TestFrameTransmitter<FaultingFrameData, 64, false>>(fixedTx)
		forwardRx = new TestFrameReceiver<64>()
		forwardTx = new TestFrameTransmitter<TestFrameReceiver<64>, 64>(forwardRx)
		service = new FrameForwarder<TestFrameReceiver<64>, TestFrameTransmitter<TestFrameReceiver<64>, 64>>(forwardRx, forwardTx)
	}

	/**
	 * Quiesces the test transmitters, closes the view and clears owned source/receiver state.
	 */
	fn Clean()
	{
		tx.BeginReset()
		tx.CompleteReset()
		tx.SetReady(true)
		fixedTx.BeginReset()
		fixedTx.CompleteReset()
		fixedTx.SetReady(true)
		region.TryClose()
		rx.BeginReset()
		rx.CompleteReset()

		if (data.IsReady()) {
			data.TryRelease()
		}
		else {
			data.TryClear()
		}

		faulty.Configure(60, -1)
	}

	/**
	 * Publishes the requested initialized prefix using deterministic high-bit octets.
	 */
	fn Prepare(length: int)
	{
		if (data.IsReady()) {
			data.TryRelease()
		}
		else {
			data.TryClear()
		}

		for (var i = 0; i < length; i++) {
			assert data.TryWrite(i, (128 + i) as byte) == PacketDataResult.Success
		}

		assert data.TryPublish() == PacketDataResult.Success
	}

	/**
	 * Checks acquisition, valid-prefix reads and the distinction between known and unknown wire length.
	 */
	@Test
	fn ReceiveOwnershipAndLengthKnowledge()
	{
		this.Clean()
		var value: byte = 0x00
		rxChecks.Empty()
		assert rx.GetReceiveCapacity() == 64
		assert rx.TryAcquire() == FrameLinkResult.NotReady
		assert rx.TryRead(0, value) == PacketDataResult.NotReady
		assert rx.TryRelease() == FrameLinkResult.Invalid
		assert rx.InjectByte(1, 0xFF) == FrameLinkResult.Invalid
		assert rx.InjectByte(0, 0x80) == FrameLinkResult.Success
		assert rx.InjectByte(1, 0xFF) == FrameLinkResult.Success
		assert rx.Publish(true, 1) == FrameLinkResult.Invalid
		assert rx.Publish(false, 60) == FrameLinkResult.Invalid
		assert rx.Publish(true, 60) == FrameLinkResult.Success
		assert rx.HasReceiveFrame() == true
		assert rx.IsReady() == false
		assert rx.GetAvailableLength() == 0
		assert rx.TryRead(0, value) == PacketDataResult.NotReady
		assert rx.TryRelease() == FrameLinkResult.Invalid
		assert rx.TryAcquire() == FrameLinkResult.Success
		assert rx.TryAcquire() == FrameLinkResult.Busy
		rxChecks.Acquired(2, true, 60)
		assert rx.GetAvailableLength() == 2
		assert rx.HasKnownWireLength() == true
		assert rx.GetWireLength() == 60
		assert rx.InjectByte(0, 0x00) == FrameLinkResult.Busy
		assert rx.Publish(false, 0) == FrameLinkResult.Busy
		assert rx.FailReceive() == FrameLinkResult.Busy
		assert region.TryOpen(1, 1) == PacketDataResult.Success
		assert region.TryRead(0, value) == PacketDataResult.Success
		assert value == 0xFF
		assert rx.TryRead(2, value) == PacketDataResult.Invalid
		assert rx.TryRead(-1, value) == PacketDataResult.Invalid
		assert region.TryClose() == PacketDataResult.Success
		assert rx.TryRelease() == FrameLinkResult.Success
		assert rx.HasKnownWireLength() == false
		assert rx.TryRead(0, value) == PacketDataResult.NotReady
		assert rx.InjectByte(0, 0x42) == FrameLinkResult.Success
		assert rx.Publish(false, 0) == FrameLinkResult.Success
		assert rx.TryAcquire() == FrameLinkResult.Success
		assert rx.HasKnownWireLength() == false
		rxChecks.Acquired(1, false, 0)
		assert rx.GetAvailableLength() == 1
		assert rx.TryRead(0, value) == PacketDataResult.Success
		assert value == 0x42
		assert rx.TryRead(1, value) == PacketDataResult.Invalid
		assert rx.TryRelease() == FrameLinkResult.Success

		// A known empty capture must be distinguishable from unknown wire length.
		assert rx.Publish(true, 0) == FrameLinkResult.Success
		assert rx.TryAcquire() == FrameLinkResult.Success
		assert rx.HasKnownWireLength() == true
		assert rx.GetWireLength() == 0
		assert rx.GetAvailableLength() == 0
		assert rx.TryRelease() == FrameLinkResult.Success
	}

	/**
	 * Checks RX error acknowledgment and that coordinated reset discards a partial capture.
	 */
	@Test
	fn ReceiveErrorsAndResetDiscardPartialData()
	{
		this.Clean()
		var value: byte = 0x00
		assert rx.InjectByte(0, 0xEE) == FrameLinkResult.Success
		assert rx.FailReceive() == FrameLinkResult.Success
		assert rx.HasReceiveError() == true
		assert rx.HasReceiveFrame() == false
		assert rx.TryAcquire() == FrameLinkResult.DeviceFailure
		assert rx.TryAcquire() == FrameLinkResult.DeviceFailure
		assert rx.Publish(false, 0) == FrameLinkResult.DeviceFailure
		assert rx.TryAcknowledgeReceiveError() == FrameLinkResult.Success
		assert rx.TryAcknowledgeReceiveError() == FrameLinkResult.NotReady
		assert rx.InjectByte(1, 0xAA) == FrameLinkResult.Invalid
		assert rx.InjectByte(0, 0x80) == FrameLinkResult.Success
		assert rx.Publish(true, 60) == FrameLinkResult.Success
		assert rx.TryAcquire() == FrameLinkResult.Success
		rx.BeginReset()
		assert rx.IsReady() == false
		assert rx.TryAcquire() == FrameLinkResult.NotReady
		assert rx.TryRelease() == FrameLinkResult.NotReady
		assert rx.InjectByte(0, 0x00) == FrameLinkResult.NotReady
		rx.CompleteReset()
		assert rx.HasKnownWireLength() == false
		assert rx.TryAcquire() == FrameLinkResult.NotReady
		assert rx.TryRead(0, value) == PacketDataResult.NotReady
		assert rx.InjectByte(1, 0xEE) == FrameLinkResult.Invalid
		assert rx.InjectByte(0, 0x11) == FrameLinkResult.Success
		assert rx.Publish(false, 0) == FrameLinkResult.Success
		assert rx.TryAcquire() == FrameLinkResult.Success
		assert rx.TryRead(0, value) == PacketDataResult.Success
		assert value == 0x11
		assert rx.TryRelease() == FrameLinkResult.Success
	}

	/**
	 * Checks admission errors before a transmitter borrows any source data.
	 */
	@Test
	fn RejectsUnpreparedEmptyAndUnsupportedSources()
	{
		this.Clean()
		assert tx.GetMinimumFrameLength() == 60
		assert tx.GetMaximumFrameLength() == 64
		assert tx.TrySubmit() == FrameLinkResult.Invalid
		assert data.TryWrite(0, 0x80) == PacketDataResult.Success
		assert tx.TrySubmit() == FrameLinkResult.Invalid
		assert tx.IsSourceBorrowed() == false
		assert data.TryPublish() == PacketDataResult.Success
		assert tx.TrySubmit() == FrameLinkResult.Unsupported
		assert data.IsReady() == true
		this.Prepare(0)
		assert tx.TrySubmit() == FrameLinkResult.Invalid
		this.Prepare(65)
		assert tx.TrySubmit() == FrameLinkResult.Unsupported
		assert tx.GetCapturedLength() == 0
		this.Prepare(60)
		tx.SetReady(false)
		assert tx.TrySubmit() == FrameLinkResult.NotReady
		assert tx.IsSourceBorrowed() == false
		tx.SetReady(true)
		tx.SetFailure(true)
		assert tx.TrySubmit() == FrameLinkResult.DeviceFailure
		assert tx.IsSourceBorrowed() == false
		tx.SetFailure(false)
		assert tx.TrySubmit() == FrameLinkResult.Accepted
		checks.Pending()
		assert tx.TryCancel() == FrameLinkResult.Accepted
		tx.Advance()
		checks.Completed(FrameTransmitOutcome.Cancelled)
		assert data.TryRelease() == PacketDataResult.Success
	}

	/**
	 * Checks source retention under backpressure and explicit acknowledgment of the terminal result.
	 */
	@Test
	fn HoldsSourceThroughBackpressureAndLatchedCompletion()
	{
		this.Clean()
		this.Prepare(64)
		var value: byte = 0x00
		assert tx.TrySubmit() == FrameLinkResult.Accepted
		checks.Pending()
		assert tx.GetCapturedLength() == 0
		assert data.TryWrite(0, 0x00) == PacketDataResult.Busy
		tx.SetReady(false)
		tx.Advance()
		assert tx.GetCapturedLength() == 0
		checks.Pending()
		tx.SetReady(true)

		for (var i = 0; i < 64; i++) {
			tx.Advance()
		}

		assert tx.GetCapturedLength() == 64

		// Reading the last byte does not yet publish a terminal result.
		checks.Pending()
		tx.Advance()
		checks.Completed(FrameTransmitOutcome.Delivered)

		for (var i = 0; i < 64; i++) {
			assert tx.TryReadCaptured(i, value) == PacketDataResult.Success
			assert value == (128 + i) as byte
		}

		assert tx.TryReadCaptured(64, value) == PacketDataResult.Invalid
		assert tx.TryReadCaptured(-1, value) == PacketDataResult.Invalid
		assert data.TryRelease() == PacketDataResult.Success
		this.Prepare(60)
		assert tx.TrySubmit() == FrameLinkResult.Accepted
		assert tx.GetCapturedLength() == 0

		for (var i = 0; i < 61; i++) {
			tx.Advance()
		}

		checks.Completed(FrameTransmitOutcome.Delivered)
		assert data.TryRelease() == PacketDataResult.Success
	}

	/**
	 * Checks that cancellation and device failure release the borrow only when reads stop.
	 */
	@Test
	fn CancellationAndFailureReleaseOnlyAtTerminal()
	{
		this.Clean()
		this.Prepare(60)
		assert tx.SupportsCancellation() == true
		assert tx.TryCancel() == FrameLinkResult.Invalid
		assert tx.TrySubmit() == FrameLinkResult.Accepted
		tx.Advance()
		assert tx.TryCancel() == FrameLinkResult.Accepted
		assert tx.TryCancel() == FrameLinkResult.Busy
		checks.Pending()
		tx.SetReady(false)
		tx.Advance()
		assert tx.GetCapturedLength() == 1
		checks.Completed(FrameTransmitOutcome.Cancelled)
		tx.SetReady(true)
		assert tx.TrySubmit() == FrameLinkResult.Accepted
		tx.Advance()
		tx.SetFailure(true)
		checks.Pending()
		tx.Advance()
		checks.Completed(FrameTransmitOutcome.DeviceFailure)
		assert tx.TrySubmit() == FrameLinkResult.DeviceFailure
		tx.SetFailure(false)
		assert data.TryRelease() == PacketDataResult.Success
	}

	/**
	 * Checks unsupported cancellation and propagation of a source read failure.
	 */
	@Test
	fn UnsupportedCancellationAndReadFailure()
	{
		this.Clean()
		faulty.Configure(60, 2)
		assert fixedTx.SupportsCancellation() == false
		assert fixedTx.TrySubmit() == FrameLinkResult.Accepted
		assert faulty.GetReadCount() == 0
		assert fixedTx.TryCancel() == FrameLinkResult.Unsupported
		fixedChecks.Pending()
		fixedTx.Advance()
		fixedTx.Advance()
		assert fixedTx.GetCapturedLength() == 2
		fixedTx.Advance()
		fixedChecks.Completed(FrameTransmitOutcome.DeviceFailure)
		assert faulty.GetReadCount() == 3
		fixedTx.Advance()
		assert faulty.GetReadCount() == 3
		faulty.Configure(60, -1)
		assert fixedTx.TrySubmit() == FrameLinkResult.Accepted

		for (var i = 0; i < 61; i++) {
			fixedTx.Advance()
		}

		fixedChecks.Completed(FrameTransmitOutcome.Delivered)
		assert faulty.GetReadCount() == 60
	}

	/**
	 * Checks that beginning reset retains ownership until reset completion guarantees quiescence.
	 */
	@Test
	fn ResetBarrierKeepsBorrowUntilQuiescent()
	{
		this.Clean()
		this.Prepare(60)
		var outcome: FrameTransmitOutcome = FrameTransmitOutcome.Delivered
		assert tx.TrySubmit() == FrameLinkResult.Accepted
		tx.Advance()
		tx.BeginReset()
		assert tx.IsSourceBorrowed() == true
		assert tx.TrySubmit() == FrameLinkResult.NotReady
		assert tx.TryCancel() == FrameLinkResult.NotReady
		assert tx.TryGetCompletion(outcome) == FrameLinkResult.NotReady
		tx.Advance()
		assert tx.GetCapturedLength() == 1
		tx.CompleteReset()
		assert tx.IsSourceBorrowed() == false
		assert tx.GetCapturedLength() == 0
		assert tx.TryGetCompletion(outcome) == FrameLinkResult.NotReady
		assert tx.TrySubmit() == FrameLinkResult.NotReady
		assert data.TryRelease() == PacketDataResult.Success
		this.Prepare(60)
		tx.SetReady(true)
		assert tx.TrySubmit() == FrameLinkResult.Accepted

		for (var i = 0; i < 61; i++) {
			tx.Advance()
		}

		// Reset also discards a terminal result rather than replaying delivery.
		tx.BeginReset()
		tx.CompleteReset()
		assert tx.TryGetCompletion(outcome) == FrameLinkResult.NotReady
		assert data.TryRelease() == PacketDataResult.Success
	}

	/**
	 * Forwards a complete received frame without test-adapter controls in the service implementation.
	 */
	@Test
	fn ServiceUsesOnlyReceiverAndTransmitterCapabilities()
	{
		var outcome: FrameTransmitOutcome = FrameTransmitOutcome.DeviceFailure
		var value: byte = 0x00
		assert service.TryStart() == FrameLinkResult.NotReady

		for (var i = 0; i < 60; i++) {
			assert forwardRx.InjectByte(i, (i + 128) as byte) == FrameLinkResult.Success
		}

		assert forwardRx.Publish(true, 60) == FrameLinkResult.Success
		assert service.TryStart() == FrameLinkResult.Accepted
		assert service.TryStart() == FrameLinkResult.Busy
		assert service.TryFinish(outcome) == FrameLinkResult.NotReady
		assert forwardRx.IsReady() == true

		for (var i = 0; i < 61; i++) {
			forwardTx.Advance()
		}

		assert service.TryFinish(outcome) == FrameLinkResult.Success
		assert outcome == FrameTransmitOutcome.Delivered
		assert forwardRx.IsReady() == false
		assert forwardRx.HasReceiveFrame() == false

		for (var i = 0; i < 60; i++) {
			assert forwardTx.TryReadCaptured(i, value) == PacketDataResult.Success
			assert value == (i + 128) as byte
		}

		assert service.TryFinish(outcome) == FrameLinkResult.NotReady

		// Rejected submission releases the service's RX acquisition for reuse.
		assert forwardRx.InjectByte(0, 0xFF) == FrameLinkResult.Success
		assert forwardRx.Publish(true, 60) == FrameLinkResult.Success
		assert service.TryStart() == FrameLinkResult.Invalid
		assert forwardRx.HasReceiveFrame() == false
		assert forwardTx.IsSourceBorrowed() == false

		for (var i = 0; i < 60; i++) {
			assert forwardRx.InjectByte(i, 0x00) == FrameLinkResult.Success
		}

		assert forwardRx.Publish(true, 60) == FrameLinkResult.Success
		forwardTx.SetReady(false)
		assert service.TryStart() == FrameLinkResult.NotReady
		assert forwardRx.HasReceiveFrame() == false
		forwardTx.SetReady(true)
	}
}