This commit is contained in:
+361
@@ -0,0 +1,361 @@
|
||||
package haus.nightmare.lib3270j.datastream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.graphics.GocaConstants;
|
||||
import haus.nightmare.lib3270j.graphics.GraphicsMode;
|
||||
import haus.nightmare.lib3270j.printer.PrintSCS3270;
|
||||
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Comprehensive test suite verifying all Phase 2 functional updates:
|
||||
* 3270 Data Stream parsing, Graphic Escape & APL mapping, Partitioning,
|
||||
* Structured Fields (0x0F, 0x20, 0x24, 0x40), and Query Reply builders.
|
||||
*/
|
||||
public class DataStreamProcessorPhase2FullTest {
|
||||
|
||||
private EbcdicTranslator translator;
|
||||
private ScreenBuffer screen;
|
||||
private DataStreamProcessor processor;
|
||||
private ByteArrayOutputStream output;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3279_4, translator);
|
||||
processor = new DataStreamProcessor(screen, translator);
|
||||
output = new ByteArrayOutputStream();
|
||||
processor.setOutputSender(bytes -> output.write(bytes, 0, bytes.length));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGraphicEscapeOrderWithAPLTranslation() {
|
||||
// Write record: CMD_EW (0x05), WCC (0xC3), SBA to 0 (0x11, 0x40, 0x40), ORDER_GE (0x08), 0xA2 (s -> ─)
|
||||
// followed by ORDER_GE, 0x85 (e -> │), ORDER_GE, 0xCB (Cross -> ┼), ORDER_GE, 0xBA (Omega -> Ω)
|
||||
byte[] record = new byte[] {
|
||||
(byte) CMD_EW, (byte) 0xC3,
|
||||
(byte) ORDER_SBA, 0x40, 0x40,
|
||||
(byte) ORDER_GE, (byte) 0xA2, // Horizontal Line
|
||||
(byte) ORDER_GE, (byte) 0x85, // Vertical Line
|
||||
(byte) ORDER_GE, (byte) 0xCB, // Cross
|
||||
(byte) ORDER_GE, (byte) 0xBA // Greek Omega
|
||||
};
|
||||
|
||||
processor.processRecord(record);
|
||||
|
||||
ExtendedAttribute cell0 = screen.getCell(0);
|
||||
assertEquals(CS_GE, cell0.cs);
|
||||
assertEquals((byte) 0xA2, cell0.ec);
|
||||
assertEquals('─', cell0.ucs4);
|
||||
|
||||
ExtendedAttribute cell1 = screen.getCell(1);
|
||||
assertEquals(CS_GE, cell1.cs);
|
||||
assertEquals((byte) 0x85, cell1.ec);
|
||||
assertEquals('│', cell1.ucs4);
|
||||
|
||||
ExtendedAttribute cell2 = screen.getCell(2);
|
||||
assertEquals(CS_GE, cell2.cs);
|
||||
assertEquals((byte) 0xCB, cell2.ec);
|
||||
assertEquals('┼', cell2.ucs4);
|
||||
|
||||
ExtendedAttribute cell3 = screen.getCell(3);
|
||||
assertEquals(CS_GE, cell3.cs);
|
||||
assertEquals((byte) 0xBA, cell3.ec);
|
||||
assertEquals('Ω', cell3.ucs4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatToAddressWithGraphicEscape() {
|
||||
// Repeat to address 5 with ORDER_GE prefix + 0xA2 ('─')
|
||||
byte[] record = new byte[] {
|
||||
(byte) CMD_EW, (byte) 0xC3,
|
||||
(byte) ORDER_SBA, 0x40, 0x40, // Addr 0
|
||||
(byte) ORDER_RA, 0x40, (byte) 0x45, // Addr 5
|
||||
(byte) ORDER_GE, (byte) 0xA2
|
||||
};
|
||||
|
||||
processor.processRecord(record);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ExtendedAttribute cell = screen.getCell(i);
|
||||
assertEquals(CS_GE, cell.cs, "Cell " + i + " should have CS_GE");
|
||||
assertEquals('─', cell.ucs4, "Cell " + i + " should have '─'");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutbound3270DSNonZeroPartition() {
|
||||
// First create partition 2 (32 rows x 80 cols)
|
||||
byte[] createPart = new byte[] {
|
||||
(byte) CMD_WSF,
|
||||
0x00, 0x08,
|
||||
(byte) SF_CREATE_PART, 0x02,
|
||||
0x00, 80,
|
||||
0x00, 32
|
||||
};
|
||||
processor.processRecord(createPart);
|
||||
assertEquals(2, screen.getActivePartition());
|
||||
|
||||
// Now activate partition 0
|
||||
screen.setActivePartition(0);
|
||||
assertEquals(0, screen.getActivePartition());
|
||||
|
||||
// Send Outbound 3270DS targeting Partition 2 containing Write command to write 'H' at pos 0
|
||||
byte[] outboundDs = new byte[] {
|
||||
(byte) CMD_WSF,
|
||||
0x00, 0x0A, // Field length = 10
|
||||
(byte) SF_OUTBOUND_DS, 0x02, // Target PID = 2
|
||||
(byte) CMD_W, 0x00, // Write command with null WCC
|
||||
(byte) ORDER_SBA, 0x40, 0x40, // Pos 0
|
||||
(byte) 0xC8 // EBCDIC 'H'
|
||||
};
|
||||
|
||||
processor.processRecord(outboundDs);
|
||||
|
||||
// Verify active partition was switched to PID 2
|
||||
assertEquals(2, screen.getActivePartition());
|
||||
assertEquals('H', screen.getCell(0).ucs4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStructuredField0x0FSetWindow() {
|
||||
// SF 0x0F with explicit viewport window (xmin=10, ymin=20, xmax=500, ymax=300)
|
||||
byte[] setWindowSf = new byte[] {
|
||||
(byte) CMD_WSF,
|
||||
0x00, 0x0B, // Length = 11
|
||||
(byte) SF_SET_WINDOW,
|
||||
0x00, 10, // xMin = 10
|
||||
0x00, 20, // yMin = 20
|
||||
0x01, (byte) 0xF4, // xMax = 500
|
||||
0x01, 0x2C // yMax = 300
|
||||
};
|
||||
|
||||
processor.processRecord(setWindowSf);
|
||||
|
||||
// Test direct overload processSFSetWindow
|
||||
processor.processSFSetWindow(setWindowSf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStructuredField0x0FDataUnitActivatesGraphicCursor() {
|
||||
assertFalse(processor.getGocaDecoder().isGraphicsCursorActive());
|
||||
|
||||
byte[] objDataSf = new byte[] {
|
||||
(byte) CMD_WSF,
|
||||
0x00, 0x04,
|
||||
(byte) SF_SET_WINDOW,
|
||||
(byte) GocaConstants.SF_OBJDATA_SUB // 0x0F
|
||||
};
|
||||
|
||||
processor.processRecord(objDataSf);
|
||||
assertTrue(processor.getGocaDecoder().isGraphicsCursorActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStructuredField0x20ObjectControl() {
|
||||
// SF 0x20 (3270 Graphics / Object Control) with GOCA begin/end area orders
|
||||
byte[] objControl = new byte[] {
|
||||
(byte) CMD_WSF,
|
||||
0x00, 0x07,
|
||||
(byte) SF_OBJECT_CONTROL,
|
||||
(byte) GocaConstants.G_GBAR, 0x00, // Begin area
|
||||
(byte) GocaConstants.G_GEAR, 0x00 // End area
|
||||
};
|
||||
|
||||
processor.processRecord(objControl);
|
||||
processor.processSFObjectControl(objControl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStructuredField0x24DocumentDataWithEmbeddedSCS() {
|
||||
AtomicReference<byte[]> scsCaptured = new AtomicReference<>();
|
||||
PrintSCS3270 mockScs = new PrintSCS3270(null, null, translator) {
|
||||
@Override
|
||||
public void processHostData(byte[] data, int offset, int length) {
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(data, offset, b, 0, length);
|
||||
scsCaptured.set(b);
|
||||
}
|
||||
};
|
||||
processor.setEmbeddedScsProcessor(mockScs);
|
||||
|
||||
// SF 0x24 (Document Data) with 4 bytes of SCS printer data
|
||||
byte[] docData = new byte[] {
|
||||
(byte) CMD_WSF,
|
||||
0x00, 0x07,
|
||||
(byte) SF_DOCUMENT_DATA,
|
||||
(byte) 0x15, (byte) 0xC8, (byte) 0xC9, (byte) 0x15 // NL, 'H', 'I', NL
|
||||
};
|
||||
|
||||
processor.processRecord(docData);
|
||||
assertNotNull(scsCaptured.get());
|
||||
assertEquals(4, scsCaptured.get().length);
|
||||
assertEquals(0x15, scsCaptured.get()[0]);
|
||||
assertEquals((byte) 0xC8, scsCaptured.get()[1]);
|
||||
|
||||
// Test direct method
|
||||
processor.processSFDocumentData(docData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryReplyBuilderGraphicColor109Bytes() {
|
||||
QueryReplyBuilder qrBuilder = processor.getQueryReplyBuilder();
|
||||
byte[] grColor = qrBuilder.buildGraphicColor();
|
||||
|
||||
assertNotNull(grColor);
|
||||
assertEquals(105, grColor.length); // 105 bytes payload + 4 bytes header = 109 bytes in complete SF
|
||||
|
||||
// Check header bytes in payload: 0x00, 0x04, 0x00, 0xFF, 0xFF, 0x00, 0x10, 0x00, 0x10
|
||||
assertEquals(0x00, grColor[0]);
|
||||
assertEquals(0x04, grColor[1]);
|
||||
assertEquals(0x00, grColor[2]);
|
||||
assertEquals((byte) 0xFF, grColor[3]);
|
||||
assertEquals((byte) 0xFF, grColor[4]);
|
||||
assertEquals(0x00, grColor[5]);
|
||||
assertEquals(0x10, grColor[6]);
|
||||
assertEquals(0x00, grColor[7]);
|
||||
assertEquals(0x10, grColor[8]);
|
||||
|
||||
// Check aliases
|
||||
assertArrayEquals(grColor, qrBuilder.buildGrColor());
|
||||
assertArrayEquals(grColor, qrBuilder.buildGColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryReplyBuilderAuxDeviceAndLineType() {
|
||||
QueryReplyBuilder qrBuilder = processor.getQueryReplyBuilder();
|
||||
byte[] lineType = qrBuilder.buildAuxDevice();
|
||||
|
||||
assertNotNull(lineType);
|
||||
assertEquals(20, lineType.length); // 20 bytes payload + 4 bytes header = 24 bytes
|
||||
|
||||
assertArrayEquals(lineType, qrBuilder.buildLineType());
|
||||
assertArrayEquals(lineType, qrBuilder.buildAuxDev());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryReplyBuilderOemFormatAndPort() {
|
||||
QueryReplyBuilder qrBuilder = processor.getQueryReplyBuilder();
|
||||
byte[] port = qrBuilder.buildOemFormat();
|
||||
|
||||
assertNotNull(port);
|
||||
assertTrue(port.length > 0);
|
||||
assertArrayEquals(port, qrBuilder.buildPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryReplyBuilderUsableAreaAspectRatiosAndNoArgOverloads() {
|
||||
QueryReplyBuilder qrBuilder = processor.getQueryReplyBuilder();
|
||||
|
||||
// 1. Vector Graphics enabled (3179G standard: SDH = 16, flags = 0x03)
|
||||
qrBuilder.setGraphicsMode(GraphicsMode.VECTOR_GRAPHICS);
|
||||
byte[] uaVector = qrBuilder.buildUsableArea();
|
||||
assertNotNull(uaVector);
|
||||
assertEquals(19, uaVector.length);
|
||||
assertEquals(0x03, uaVector[0] & 0xFF); // Flags: 12/14 bit + Graphics
|
||||
assertEquals(9, uaVector[15] & 0xFF); // AW = 9
|
||||
assertEquals(16, uaVector[16] & 0xFF); // AH = 16
|
||||
|
||||
// 2. Text mode (GraphicsMode.NONE: SDH = 12, flags = 0x01)
|
||||
qrBuilder.setGraphicsMode(GraphicsMode.NONE);
|
||||
byte[] uaText = qrBuilder.buildUsableArea();
|
||||
assertNotNull(uaText);
|
||||
assertEquals(0x01, uaText[0] & 0xFF); // Flags: 12/14 bit only
|
||||
assertEquals(9, uaText[15] & 0xFF); // AW = 9
|
||||
assertEquals(12, uaText[16] & 0xFF); // AH = 12
|
||||
|
||||
// 3. No-arg overloads check
|
||||
assertNotNull(qrBuilder.buildSummary());
|
||||
assertNotNull(qrBuilder.buildAlphaPartitions());
|
||||
assertNotNull(qrBuilder.buildCharsets());
|
||||
assertNotNull(qrBuilder.buildColor());
|
||||
assertNotNull(qrBuilder.buildHighlighting());
|
||||
assertNotNull(qrBuilder.buildReplyModes());
|
||||
assertNotNull(qrBuilder.buildDdm());
|
||||
assertNotNull(qrBuilder.buildImplicitPartition());
|
||||
assertNotNull(qrBuilder.buildSegment());
|
||||
assertNotNull(qrBuilder.buildProcedure());
|
||||
assertNotNull(qrBuilder.buildGraphics());
|
||||
assertNotNull(qrBuilder.buildGImage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryReplyBuilderColorTable18Bytes() {
|
||||
QueryReplyBuilder qrBuilder = processor.getQueryReplyBuilder();
|
||||
byte[] colorTable = qrBuilder.buildColor();
|
||||
|
||||
assertNotNull(colorTable);
|
||||
assertEquals(18, colorTable.length, "Color table payload must be exactly 18 bytes matching HoD DS3270.java:1850");
|
||||
assertEquals(0x00, colorTable[0]);
|
||||
assertEquals(0x08, colorTable[1]);
|
||||
assertEquals(0x00, colorTable[2]);
|
||||
assertEquals((byte) 0xF4, colorTable[3]); // Default green
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataStreamProcessorConvenienceOverloads() {
|
||||
// Test processEraseAllUnprotected
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.getCell(1).ucs4 = 'X';
|
||||
screen.getCell(1).ec = (byte) 0xE7;
|
||||
processor.processEraseAllUnprotected();
|
||||
assertEquals(0, screen.getCell(1).ucs4);
|
||||
|
||||
// Test processReadModified and processReadModifiedAll
|
||||
output.reset();
|
||||
processor.processReadModified();
|
||||
assertTrue(output.size() >= 3);
|
||||
|
||||
output.reset();
|
||||
processor.processReadModifiedAll();
|
||||
assertTrue(output.size() >= 3);
|
||||
|
||||
// Test processReadBuffer
|
||||
output.reset();
|
||||
processor.processReadBuffer();
|
||||
assertTrue(output.size() >= 3);
|
||||
|
||||
// Test processEraseWrite and processEraseWriteAlternate
|
||||
byte[] ewData = new byte[] { (byte) CMD_EW, 0x00, (byte) ORDER_SBA, 0x40, 0x40, (byte) 0xC1 };
|
||||
processor.processEraseWrite(ewData);
|
||||
assertEquals('A', screen.getCell(0).ucs4);
|
||||
|
||||
byte[] ewaData = new byte[] { (byte) CMD_EWA, 0x00, (byte) ORDER_SBA, 0x40, 0x40, (byte) 0xC2 };
|
||||
processor.processEraseWriteAlternate(ewaData);
|
||||
assertEquals('B', screen.getCell(0).ucs4);
|
||||
|
||||
// Test processSetReplyMode, processCreatePartition, processEraseReset byte[] overloads
|
||||
byte[] srm = new byte[] { (byte) CMD_WSF, 0x00, 0x05, (byte) SF_SET_REPLY_MODE, 0x00, (byte) SF_SRM_CHAR };
|
||||
processor.processSetReplyMode(srm);
|
||||
assertEquals((byte) SF_SRM_CHAR, screen.getReplyMode());
|
||||
|
||||
byte[] cp = new byte[] { (byte) CMD_WSF, 0x00, 0x08, (byte) SF_CREATE_PART, 0x01, 0x00, 80, 0x00, 24 };
|
||||
processor.processCreatePartition(cp);
|
||||
assertEquals(1, screen.getActivePartition());
|
||||
|
||||
byte[] er = new byte[] { (byte) CMD_WSF, 0x00, 0x04, (byte) SF_ERASE_RESET, (byte) SF_ER_DEFAULT };
|
||||
processor.processEraseReset(er);
|
||||
assertEquals(0, screen.getActivePartition());
|
||||
|
||||
// Test query partition overloads
|
||||
output.reset();
|
||||
processor.processSFReadPartitionQuery(new byte[] { (byte) CMD_WSF, 0x00, 0x05, (byte) SF_READ_PART, 0x00, (byte) SF_RP_QUERY });
|
||||
assertTrue(output.size() > 0);
|
||||
|
||||
output.reset();
|
||||
processor.processSFReadPartitionQueryList(new byte[] {
|
||||
(byte) CMD_WSF, 0x00, 0x07, (byte) SF_READ_PART, 0x00, (byte) SF_RP_QLIST, (byte) SF_RPQ_LIST, (byte) QR_COLOR
|
||||
});
|
||||
assertTrue(output.size() > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package haus.nightmare.lib3270j.ecl;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static haus.nightmare.lib3270j.ecl.ECLConstants.*;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
public class ECLOIAPhase3Test {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private EbcdicTranslator translator;
|
||||
private InputProcessor input;
|
||||
private ECLOIA oia;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||
input = new InputProcessor(screen, translator, null);
|
||||
oia = new ECLOIA(screen, input, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlphanumericTypeDetection() {
|
||||
screen.erase(false);
|
||||
// Unformatted screen
|
||||
assertEquals(TYPE_ALPHANUMERIC, oia.getAlphanumericType());
|
||||
assertEquals("A", oia.getAlphanumericTypeString());
|
||||
|
||||
// Numeric field
|
||||
screen.setFieldAttribute(0, (byte) (FA_PRINTABLE | FA_NUMERIC));
|
||||
screen.setCursorAddress(1);
|
||||
assertEquals(TYPE_NUMERIC, oia.getAlphanumericType());
|
||||
assertEquals("N", oia.getAlphanumericTypeString());
|
||||
|
||||
// Alphanumeric unprotected field
|
||||
screen.setFieldAttribute(10, (byte) FA_PRINTABLE);
|
||||
screen.setCursorAddress(11);
|
||||
assertEquals(TYPE_ALPHANUMERIC, oia.getAlphanumericType());
|
||||
assertEquals("A", oia.getAlphanumericTypeString());
|
||||
|
||||
// DBCS cell
|
||||
ExtendedAttribute ea = screen.getCell(11);
|
||||
ea.cs = ExtendedAttribute.CS_DBCS;
|
||||
assertEquals(TYPE_DBCS, oia.getAlphanumericType());
|
||||
assertEquals("D", oia.getAlphanumericTypeString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOIAStatusFlagsAndStrings() {
|
||||
screen.erase(false);
|
||||
assertEquals("READY", oia.getStatusString());
|
||||
assertFalse(oia.isXSystem());
|
||||
assertFalse(oia.isXProt());
|
||||
assertFalse(oia.isXNum());
|
||||
assertFalse(oia.isXComm());
|
||||
assertFalse(oia.isXOverflow());
|
||||
assertFalse(oia.isXOperatorDue());
|
||||
|
||||
// Keyboard lock -> X-SYSTEM
|
||||
input.setKeyboardLocked(true);
|
||||
assertTrue(oia.isXSystem());
|
||||
assertTrue(oia.isXWait());
|
||||
assertEquals("X-SYSTEM", oia.getStatusString());
|
||||
input.setKeyboardLocked(false);
|
||||
|
||||
// Protected field inhibit
|
||||
oia.setInputInhibited(INHIBIT_PROTECTED_FIELD);
|
||||
assertTrue(oia.isXProt());
|
||||
assertEquals("X-PROT", oia.getStatusString());
|
||||
|
||||
// Numeric only inhibit
|
||||
oia.setInputInhibited(INHIBIT_NUMERIC_ONLY);
|
||||
assertTrue(oia.isXNum());
|
||||
assertEquals("X-NUM", oia.getStatusString());
|
||||
|
||||
// Overflow inhibit
|
||||
oia.setInputInhibited(INHIBIT_OVERFLOW);
|
||||
assertTrue(oia.isXOverflow());
|
||||
assertEquals("X-OVERFLOW", oia.getStatusString());
|
||||
|
||||
// Comm check inhibit
|
||||
oia.setInputInhibited(INHIBIT_COMM_CHECK);
|
||||
assertTrue(oia.isXComm());
|
||||
assertEquals("X-COMM", oia.getStatusString());
|
||||
|
||||
// Operator due inhibit
|
||||
oia.setInputInhibited(INHIBIT_OPERATOR_DUE);
|
||||
assertTrue(oia.isXOperatorDue());
|
||||
assertEquals("X-OP", oia.getStatusString());
|
||||
|
||||
// Insert mode
|
||||
oia.setInputInhibited(INHIBIT_NOT_INHIBITED);
|
||||
input.setInsertMode(true);
|
||||
assertTrue(oia.isXInsert());
|
||||
assertEquals("X-INSERT", oia.getStatusString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testECLFieldOperations() {
|
||||
screen.erase(false);
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.setChar(0, 1, 'X');
|
||||
screen.setChar(0, 2, 'Y');
|
||||
screen.setFieldAttribute(10, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
|
||||
ECLPS ps = new ECLPS(screen, input, translator);
|
||||
ECLField f = ps.getFieldList().getFirstField();
|
||||
assertNotNull(f);
|
||||
|
||||
assertEquals("XY ", f.getText());
|
||||
assertFalse(f.isModified());
|
||||
|
||||
f.setModified(true);
|
||||
assertTrue(f.isModified());
|
||||
|
||||
f.erase();
|
||||
assertEquals(" ", f.getText());
|
||||
assertFalse(f.isModified());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package haus.nightmare.lib3270j.ecl;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
public class ECLPSPhase3Test implements ECLConstants {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private EbcdicTranslator translator;
|
||||
private InputProcessor input;
|
||||
private ECLPS ps;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||
input = new InputProcessor(screen, translator, null);
|
||||
ps = new ECLPS(screen, input, translator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchPSOverloadsAndCaseOptions() {
|
||||
screen.erase(false);
|
||||
screen.setText("Host On-Demand ECL Automation");
|
||||
|
||||
// 1-based SearchPS
|
||||
assertEquals(6, ps.SearchPS("On-Demand"));
|
||||
assertEquals(6, ps.SearchPS("on-demand", 1, 1, SEARCH_FORWARD, true));
|
||||
assertEquals(0, ps.SearchPS("on-demand", 1, 1, SEARCH_FORWARD, false));
|
||||
assertEquals(0, ps.SearchPS("NONEXISTENT"));
|
||||
|
||||
// SearchPSExt
|
||||
int pos = ps.SearchPSExt("ECL", 1, 50, SEARCH_FORWARD, false, false);
|
||||
assertEquals(16, pos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchStringBackwardsAndWrap() {
|
||||
screen.erase(false);
|
||||
screen.setChar(0, 10, 'A');
|
||||
screen.setChar(0, 11, 'B');
|
||||
screen.setChar(0, 12, 'C');
|
||||
|
||||
screen.setChar(1, 20, 'A');
|
||||
screen.setChar(1, 21, 'B');
|
||||
screen.setChar(1, 22, 'C');
|
||||
|
||||
// Backward search starting from row 1 col 0 (addr 80) should find "ABC" at row 0 col 10 (addr 10)
|
||||
int found = ps.searchString("ABC", 1, 0, SEARCH_BACKWARD, false);
|
||||
assertEquals(10, found);
|
||||
|
||||
// Backward search starting from row 0 col 5 (addr 5) wraps around and finds "ABC" at row 1 col 20 (addr 100)
|
||||
int wrapped = ps.searchString("ABC", 0, 5, SEARCH_BACKWARD, false);
|
||||
assertEquals(100, wrapped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyStringRectangularExtraction() {
|
||||
screen.erase(false);
|
||||
// Row 0: "0123456789"
|
||||
for (int c = 0; c < 10; c++) {
|
||||
screen.setChar(0, c, (char) ('0' + c));
|
||||
}
|
||||
// Row 1: "ABCDEFGHIJ"
|
||||
for (int c = 0; c < 10; c++) {
|
||||
screen.setChar(1, c, (char) ('A' + c));
|
||||
}
|
||||
|
||||
// Copy columns 2..5 of rows 0..1
|
||||
String block = ps.copyString(0, 2, 1, 5);
|
||||
assertEquals("2345\nCDEF", block);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPasteStringRectangular() {
|
||||
screen.erase(false);
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.setFieldAttribute(80, (byte) FA_PRINTABLE);
|
||||
|
||||
int pasted = ps.pasteString("HELLO\nWORLD", 0, 1);
|
||||
assertEquals(10, pasted);
|
||||
|
||||
assertEquals("HELLO", ps.getString(1, 5));
|
||||
assertEquals("WORLD", ps.getString(81, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendCharactersWithDelay() {
|
||||
screen.erase(false);
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.setFieldAttribute(10, (byte) FA_PRINTABLE);
|
||||
screen.setCursorAddress(1);
|
||||
|
||||
ps.sendCharacters("ABC[tab]DEF", 2);
|
||||
assertEquals('A', screen.getChar(0, 1));
|
||||
assertEquals('B', screen.getChar(0, 2));
|
||||
assertEquals('C', screen.getChar(0, 3));
|
||||
assertEquals('D', screen.getChar(0, 11));
|
||||
assertEquals('E', screen.getChar(0, 12));
|
||||
assertEquals('F', screen.getChar(0, 13));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitForScreenAndCursor() {
|
||||
screen.erase(false);
|
||||
assertFalse(ps.waitForScreen("READY", 50));
|
||||
assertFalse(ps.waitForCursor(5, 10, 50));
|
||||
|
||||
screen.setText("READY");
|
||||
assertTrue(ps.waitForScreen("READY", 50));
|
||||
assertTrue(ps.waitForScreen("READY", 0, 0, 50));
|
||||
assertFalse(ps.waitForScreen("READY", 1, 0, 50));
|
||||
|
||||
ps.setCursorPos(5, 10);
|
||||
assertTrue(ps.waitForCursor(5, 10, 50));
|
||||
}
|
||||
}
|
||||
@@ -288,6 +288,199 @@ public class GocaDecoderPhase5Test {
|
||||
assertEquals(56, sf2.length);
|
||||
assertEquals(0x00, sf2[0]);
|
||||
assertEquals(0x34, sf2[1]);
|
||||
|
||||
byte[] sf3 = GraphicInputBuilder.buildPickCorrelation(120, 240, 5);
|
||||
assertEquals(56, sf3.length);
|
||||
assertEquals(120, ((sf3[24] & 0xFF) << 8) | (sf3[25] & 0xFF));
|
||||
assertEquals(240, ((sf3[26] & 0xFF) << 8) | (sf3[27] & 0xFF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testViewingWindowClipping() {
|
||||
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
// Define viewing window covering center of presentation space: [-100..100, -100..100]
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(GocaConstants.G_GSVW_DEF);
|
||||
out.write(0x08);
|
||||
out.write((byte) 0xFF); out.write((byte) 0x9C); // xMin = -100
|
||||
out.write((byte) 0xFF); out.write((byte) 0x9C); // yMin = -100
|
||||
out.write(0x00); out.write(100); // xMax = 100
|
||||
out.write(0x00); out.write(100); // yMax = 100
|
||||
|
||||
// Draw a line inside presentation space but outside the viewing window: (-300, -150) -> (-200, -150)
|
||||
out.write(GocaConstants.G_GSCOL); out.write(0x02); // Red
|
||||
out.write(GocaConstants.G_GLINE); out.write(0x08);
|
||||
out.write((byte) 0xFE); out.write((byte) 0xD4); out.write((byte) 0xFF); out.write((byte) 0x6A); // (-300, -150)
|
||||
out.write((byte) 0xFF); out.write((byte) 0x38); out.write((byte) 0xFF); out.write((byte) 0x6A); // (-200, -150)
|
||||
|
||||
byte[] stream = out.toByteArray();
|
||||
decoder.decodeGoca(stream, 0, stream.length);
|
||||
|
||||
// Pixels outside viewing window should have been clipped out
|
||||
int pxClipped = plane.mapX(-300);
|
||||
int pyClipped = plane.mapY(-150);
|
||||
int pixelOutside = plane.getRgbBuffer()[pyClipped * plane.getCanvasWidth() + pxClipped];
|
||||
assertEquals(0, pixelOutside, "Pixel outside viewing window must be 0 (clipped)");
|
||||
|
||||
// Now draw a line crossing the center (0, 0) -> (50, 50)
|
||||
ByteArrayOutputStream insideStream = new ByteArrayOutputStream();
|
||||
insideStream.write(GocaConstants.G_GLINE); insideStream.write(0x08);
|
||||
insideStream.write(0x00); insideStream.write(0); insideStream.write(0x00); insideStream.write(0);
|
||||
insideStream.write(0x00); insideStream.write(50); insideStream.write(0x00); insideStream.write(50);
|
||||
|
||||
byte[] inBytes = insideStream.toByteArray();
|
||||
decoder.decodeGoca(inBytes, 0, inBytes.length);
|
||||
|
||||
int pxInside = plane.mapX(0);
|
||||
int pyInside = plane.mapY(0);
|
||||
int pixelInside = plane.getRgbBuffer()[pyInside * plane.getCanvasWidth() + pxInside];
|
||||
assertNotEquals(0, pixelInside, "Pixel inside viewing window must be drawn");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFractionalLineWidth() {
|
||||
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Set Fractional Line Width: 2.5 (0x02 0x80)
|
||||
out.write(GocaConstants.G_GSFLW);
|
||||
out.write(0x02);
|
||||
out.write(0x80);
|
||||
|
||||
byte[] stream = out.toByteArray();
|
||||
decoder.decodeGoca(stream, 0, stream.length);
|
||||
|
||||
assertEquals(2.5, decoder.getFractionalLineWidth(), 0.01);
|
||||
assertEquals(2.5, plane.getFractionalLineWidth(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSegmentCharacteristics() {
|
||||
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
// Flags = 0xC0: Chained (0x80), Dynamic (0x40), Visible (0x00)
|
||||
decoder.processSegmentCharacteristics(new byte[]{(byte) 0xC0});
|
||||
assertTrue(decoder.isSegChained());
|
||||
assertTrue(decoder.isSegDynamic());
|
||||
assertTrue(decoder.isSegVisible());
|
||||
|
||||
// Flags = 0x20: Invisible
|
||||
decoder.processSegmentCharacteristics(new byte[]{(byte) 0x20});
|
||||
assertFalse(decoder.isSegChained());
|
||||
assertFalse(decoder.isSegDynamic());
|
||||
assertFalse(decoder.isSegVisible());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCharacterAngleAndShear() {
|
||||
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Set Character Angle: 45 degrees vector (ax=10, ay=10)
|
||||
out.write(GocaConstants.G_GSCA);
|
||||
out.write(0x04);
|
||||
out.write(0x00); out.write(10);
|
||||
out.write(0x00); out.write(10);
|
||||
|
||||
// Set Character Shear: vector (sx=10, sy=0)
|
||||
out.write(GocaConstants.G_GSCR);
|
||||
out.write(0x04);
|
||||
out.write(0x00); out.write(10);
|
||||
out.write(0x00); out.write(0);
|
||||
|
||||
byte[] stream = out.toByteArray();
|
||||
decoder.decodeGoca(stream, 0, stream.length);
|
||||
|
||||
assertEquals(45.0, decoder.getCharAngle(), 0.01);
|
||||
assertEquals(90.0, decoder.getCharShear(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindingRuleAreaFill() {
|
||||
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||
FillArea fillArea = new FillArea();
|
||||
fillArea.setFillRule(GocaConstants.FILL_RULE_WINDING);
|
||||
assertEquals(GocaConstants.FILL_RULE_WINDING, fillArea.getFillRule());
|
||||
|
||||
// Fill self-intersecting bow-tie polygon
|
||||
int[] px = new int[]{50, 150, 50, 150};
|
||||
int[] py = new int[]{50, 150, 150, 50};
|
||||
fillArea.fill(plane, px, py, 4, 0xFF00FF00, GocaConstants.PT_SOLID, false, 0, 0, 0, 2, 0);
|
||||
assertTrue(plane.hasContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiBitAndCompressedImages() {
|
||||
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||
|
||||
// Test 2-bit per pixel image
|
||||
byte[] twoBppData = new byte[]{(byte) 0b00011011}; // 4 pixels: 0, 1 (Blue), 2 (Red), 3 (Green)
|
||||
plane.drawImage(10, 10, 4, 1, twoBppData, 0xFFFFFFFF, GocaConstants.BPP_2, GocaConstants.IMG_UNCOMPRESSED);
|
||||
assertTrue(plane.hasContent());
|
||||
|
||||
// Test 4-bit per pixel image
|
||||
byte[] fourBppData = new byte[]{(byte) 0x12, (byte) 0x34}; // 4 pixels: 1, 2, 3, 4
|
||||
plane.clear();
|
||||
plane.drawImage(10, 10, 4, 1, fourBppData, 0xFFFFFFFF, GocaConstants.BPP_4, GocaConstants.IMG_UNCOMPRESSED);
|
||||
assertTrue(plane.hasContent());
|
||||
|
||||
// Test RLE decompressed image
|
||||
byte[] rleData = new byte[]{
|
||||
0x04, (byte) 0xFF // 4 repeats of 0xFF
|
||||
};
|
||||
byte[] decompressed = GraphicsPlane.decompressGocaRle(rleData, 32, 1, GocaConstants.BPP_1);
|
||||
assertEquals(4, decompressed.length);
|
||||
assertEquals((byte) 0xFF, decompressed[0]);
|
||||
assertEquals((byte) 0xFF, decompressed[3]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiColorProgrammedSymbolsOverlay() {
|
||||
ProgramSymbolManager psm = new ProgramSymbolManager(9, 16);
|
||||
|
||||
// Load Red plane (colorPlane = 1) into Slot 4 (LCID 0x41, Start 0x40, RWS 0x04)
|
||||
byte[] loadRed = new byte[4 + 6 + 18];
|
||||
loadRed[0] = (byte) 0x81; // Extended header present (0x80) | Format 1
|
||||
loadRed[1] = 0x41; // LCID
|
||||
loadRed[2] = 0x40; // Start codepoint
|
||||
loadRed[3] = 0x04; // RWS Slot 4 (Triple Plane)
|
||||
loadRed[4] = 0x06; // Ext header length = 6
|
||||
loadRed[5] = 0x00;
|
||||
loadRed[6] = 9; // Cell width = 9
|
||||
loadRed[7] = 16; // Cell height = 16
|
||||
loadRed[8] = 0x00;
|
||||
loadRed[9] = 0x01; // Plane = Red (1)
|
||||
for (int i = 0; i < 18; i++) loadRed[10 + i] = (byte) 0xFF;
|
||||
|
||||
psm.loadps(loadRed);
|
||||
|
||||
// Load Green plane (colorPlane = 2) into the same slot without clearing (clearAll = false)
|
||||
byte[] loadGreen = new byte[4 + 6 + 18];
|
||||
loadGreen[0] = (byte) 0x81;
|
||||
loadGreen[1] = 0x41;
|
||||
loadGreen[2] = 0x40;
|
||||
loadGreen[3] = 0x04;
|
||||
loadGreen[4] = 0x06;
|
||||
loadGreen[5] = 0x00;
|
||||
loadGreen[6] = 9;
|
||||
loadGreen[7] = 16;
|
||||
loadGreen[8] = 0x00;
|
||||
loadGreen[9] = 0x02; // Plane = Green (2)
|
||||
for (int i = 0; i < 18; i++) loadGreen[10 + i] = (byte) 0xFF;
|
||||
|
||||
psm.loadps(loadGreen);
|
||||
|
||||
ProgramSymbolSet.SymbolSlot slot = psm.getSymbol(0x41, 0x40);
|
||||
assertNotNull(slot);
|
||||
byte[] pixels = slot.getPixelData();
|
||||
assertNotNull(pixels);
|
||||
// Pixels should be composite of Red (1) | Green (2) = Yellow (3)
|
||||
assertEquals(3, pixels[0] & 0xFF);
|
||||
}
|
||||
|
||||
private static byte outCoord(int val) {
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
package haus.nightmare.lib3270j.input;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.ecl.ECLConstants;
|
||||
import haus.nightmare.lib3270j.ecl.ECLOIA;
|
||||
import haus.nightmare.lib3270j.protocol.DS3270Constants;
|
||||
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class InputProcessorPhase4Test {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private EbcdicTranslator translator;
|
||||
private InputProcessor inputProcessor;
|
||||
private ECLOIA oia;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3279_2, translator);
|
||||
inputProcessor = new InputProcessor(screen, translator, null);
|
||||
oia = new ECLOIA(screen, inputProcessor, null);
|
||||
inputProcessor.setOIA(oia);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNumericOnlyValidation() {
|
||||
// Setup numeric field at pos 0 (FA), data at 1..9
|
||||
screen.setFieldAttribute(0, (byte) FA_NUMERIC);
|
||||
screen.setFieldAttribute(10, (byte) FA_PROTECT); // terminate field
|
||||
screen.setCursorAddress(1);
|
||||
|
||||
// Typing valid digits and punctuation
|
||||
inputProcessor.typeCharacter('1');
|
||||
assertEquals(2, screen.getCursorAddress());
|
||||
assertEquals('1', (char) screen.getCell(1).ucs4);
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
assertEquals(ECLConstants.INHIBIT_NOT_INHIBITED, oia.getInputInhibited());
|
||||
|
||||
inputProcessor.typeCharacter('-');
|
||||
assertEquals(3, screen.getCursorAddress());
|
||||
assertEquals('-', (char) screen.getCell(2).ucs4);
|
||||
|
||||
inputProcessor.typeCharacter('.');
|
||||
assertEquals(4, screen.getCursorAddress());
|
||||
assertEquals('.', (char) screen.getCell(3).ucs4);
|
||||
|
||||
inputProcessor.typeCharacter(' ');
|
||||
assertEquals(5, screen.getCursorAddress());
|
||||
|
||||
// Typing invalid non-numeric character 'A' in numeric field
|
||||
inputProcessor.typeCharacter('A');
|
||||
assertTrue(inputProcessor.isKeyboardLocked());
|
||||
assertEquals(ECLConstants.INHIBIT_NUMERIC_ONLY, oia.getInputInhibited());
|
||||
assertEquals("X-NUM", oia.getStatusString());
|
||||
// Buffer position 5 should NOT have changed
|
||||
assertEquals(5, screen.getCursorAddress());
|
||||
assertEquals(0, screen.getCell(5).ec);
|
||||
|
||||
// Reset clears error and unlocks keyboard
|
||||
inputProcessor.reset();
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
assertEquals(ECLConstants.INHIBIT_NOT_INHIBITED, oia.getInputInhibited());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtectedFieldInhibition() {
|
||||
// Setup protected field at pos 0, length 10
|
||||
screen.setFieldAttribute(0, (byte) FA_PROTECT);
|
||||
screen.setCursorAddress(1);
|
||||
|
||||
// Attempting to type into protected field
|
||||
inputProcessor.typeCharacter('Z');
|
||||
assertTrue(inputProcessor.isKeyboardLocked());
|
||||
assertEquals(ECLConstants.INHIBIT_PROTECTED_FIELD, oia.getInputInhibited());
|
||||
assertEquals("X-PROT", oia.getStatusString());
|
||||
assertEquals(0, screen.getCell(1).ec);
|
||||
|
||||
// Reset unlocks
|
||||
inputProcessor.reset();
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
assertEquals(ECLConstants.INHIBIT_NOT_INHIBITED, oia.getInputInhibited());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoSkipFieldBoundary() {
|
||||
// Field 1: Unprotected at pos 0 (chars at 1, 2)
|
||||
screen.setFieldAttribute(0, (byte) 0x00);
|
||||
// Field 2: Auto-skip at pos 3 (FA_PROTECT | FA_NUMERIC) (chars at 4, 5)
|
||||
screen.setFieldAttribute(3, (byte) (FA_PROTECT | FA_NUMERIC));
|
||||
// Field 3: Unprotected at pos 6 (chars at 7, 8)
|
||||
screen.setFieldAttribute(6, (byte) 0x00);
|
||||
// Delimiter at pos 9
|
||||
screen.setFieldAttribute(9, (byte) FA_PROTECT);
|
||||
|
||||
// Cursor at pos 1, type 'A' -> moves to pos 2
|
||||
screen.setCursorAddress(1);
|
||||
inputProcessor.typeCharacter('A');
|
||||
assertEquals(2, screen.getCursorAddress());
|
||||
|
||||
// Type 'B' at pos 2 (the last char in Field 1) -> auto-skips over Field 2 (pos 3..5) to pos 7!
|
||||
inputProcessor.typeCharacter('B');
|
||||
assertEquals(7, screen.getCursorAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertModeFieldOverflowInhibition() {
|
||||
// Unprotected field at pos 0, size 3 (positions 1, 2, 3)
|
||||
screen.setFieldAttribute(0, (byte) 0x00);
|
||||
screen.setFieldAttribute(4, (byte) FA_PROTECT);
|
||||
|
||||
screen.setCursorAddress(1);
|
||||
inputProcessor.typeCharacter('A');
|
||||
inputProcessor.typeCharacter('B');
|
||||
inputProcessor.typeCharacter('C');
|
||||
|
||||
// Field is full: pos 1='A', pos 2='B', pos 3='C'
|
||||
assertEquals('A', (char) screen.getCell(1).ucs4);
|
||||
assertEquals('B', (char) screen.getCell(2).ucs4);
|
||||
assertEquals('C', (char) screen.getCell(3).ucs4);
|
||||
|
||||
// Enable insert mode and attempt to type at pos 1
|
||||
inputProcessor.setInsertMode(true);
|
||||
screen.setCursorAddress(1);
|
||||
inputProcessor.typeCharacter('X');
|
||||
|
||||
assertTrue(inputProcessor.isKeyboardLocked());
|
||||
assertEquals(ECLConstants.INHIBIT_OVERFLOW, oia.getInputInhibited());
|
||||
assertEquals("X-OVERFLOW", oia.getStatusString());
|
||||
assertEquals('A', (char) screen.getCell(1).ucs4); // unchanged
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadModifiedInboundDataFraming() {
|
||||
// Create field at pos 0, type "TEST"
|
||||
screen.setFieldAttribute(0, (byte) 0x00);
|
||||
screen.setFieldAttribute(10, (byte) FA_PROTECT);
|
||||
screen.setCursorAddress(1);
|
||||
inputProcessor.typeCharacter('T');
|
||||
inputProcessor.typeCharacter('E');
|
||||
inputProcessor.typeCharacter('S');
|
||||
inputProcessor.typeCharacter('T');
|
||||
|
||||
byte[] inbound = inputProcessor.buildReadModifiedInboundData(AID_ENTER, false);
|
||||
assertNotNull(inbound);
|
||||
assertTrue(inbound.length >= 7);
|
||||
assertEquals((byte) AID_ENTER, inbound[0]); // AID
|
||||
// Next 2 bytes: cursor address
|
||||
// Next byte: SBA (0x11)
|
||||
assertEquals((byte) ORDER_SBA, inbound[3]);
|
||||
// Followed by field address (pos 1), then EBCDIC bytes for "TEST"
|
||||
assertEquals((byte) translator.unicodeToEbcdic('T'), inbound[6]);
|
||||
assertEquals((byte) translator.unicodeToEbcdic('E'), inbound[7]);
|
||||
assertEquals((byte) translator.unicodeToEbcdic('S'), inbound[8]);
|
||||
assertEquals((byte) translator.unicodeToEbcdic('T'), inbound[9]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAidSelectDoesNotTransmitFieldCharacters() {
|
||||
// Create selectable field with designator ' ' at pos 1
|
||||
screen.setFieldAttribute(0, (byte) (FA_INT_HIGH_SEL | FA_NUMERIC)); // selectable
|
||||
screen.getCell(0).fa |= FA_MODIFY;
|
||||
screen.getCell(1).ec = 0x40; // space designator
|
||||
screen.getCell(2).ec = (byte) translator.unicodeToEbcdic('X');
|
||||
screen.setFieldAttribute(10, (byte) FA_PROTECT);
|
||||
|
||||
screen.setCursorAddress(1);
|
||||
byte[] inbound = inputProcessor.buildReadModifiedInboundData(AID_SELECT, false);
|
||||
assertNotNull(inbound);
|
||||
assertEquals((byte) AID_SELECT, inbound[0]);
|
||||
// Must contain SBA and designator address, but NO field text bytes
|
||||
assertEquals(6, inbound.length); // AID (1) + Cursor (2) + SBA (1) + Addr (2) = 6 bytes
|
||||
assertEquals((byte) ORDER_SBA, inbound[3]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPAKeysAndClearInboundData() {
|
||||
screen.setCursorAddress(80);
|
||||
byte[] pa1 = inputProcessor.buildReadModifiedInboundData(AID_PA1, false);
|
||||
assertEquals(3, pa1.length); // AID + 2 cursor bytes
|
||||
assertEquals((byte) AID_PA1, pa1[0]);
|
||||
|
||||
byte[] clear = inputProcessor.buildReadModifiedInboundData(AID_CLEAR, false);
|
||||
assertEquals(3, clear.length);
|
||||
assertEquals((byte) AID_CLEAR, clear[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBufferInboundDataStandardAndExtended() {
|
||||
screen.setFieldAttribute(0, (byte) 0x00);
|
||||
screen.getCell(1).ec = (byte) translator.unicodeToEbcdic('A');
|
||||
screen.setFieldAttribute(10, (byte) FA_PROTECT);
|
||||
|
||||
// Standard Field Mode
|
||||
screen.setReplyMode((byte) SF_SRM_FIELD);
|
||||
byte[] standardBuf = inputProcessor.buildReadBufferInboundData(AID_ENTER);
|
||||
assertNotNull(standardBuf);
|
||||
assertEquals((byte) AID_ENTER, standardBuf[0]);
|
||||
assertEquals((byte) ORDER_SF, standardBuf[3]);
|
||||
|
||||
// Extended Field Mode
|
||||
screen.setReplyMode((byte) SF_SRM_XFIELD);
|
||||
byte[] extendedBuf = inputProcessor.buildReadBufferInboundData(AID_ENTER);
|
||||
assertNotNull(extendedBuf);
|
||||
assertEquals((byte) AID_ENTER, extendedBuf[0]);
|
||||
assertEquals((byte) ORDER_SFE, extendedBuf[3]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWordLeftWordRightAndFieldEndNavigation() {
|
||||
// Field: "HELLO WORLD "
|
||||
screen.setFieldAttribute(0, (byte) 0x00);
|
||||
screen.setFieldAttribute(25, (byte) FA_PROTECT);
|
||||
|
||||
String text = "HELLO WORLD ";
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
screen.getCell(1 + i).ec = (byte) translator.unicodeToEbcdic(text.charAt(i));
|
||||
screen.getCell(1 + i).ucs4 = text.charAt(i);
|
||||
}
|
||||
|
||||
// Test Field End
|
||||
screen.setCursorAddress(1);
|
||||
inputProcessor.processFieldEnd();
|
||||
// Immediately following 'D' in WORLD (pos 1 + 13 = 14)
|
||||
assertEquals(14, screen.getCursorAddress());
|
||||
|
||||
// Test Word Left from pos 14 -> jumps to start of "WORLD" (pos 9)
|
||||
inputProcessor.processWordLeft();
|
||||
assertEquals(9, screen.getCursorAddress());
|
||||
|
||||
// Test Word Left from pos 9 -> jumps to start of "HELLO" (pos 1)
|
||||
inputProcessor.processWordLeft();
|
||||
assertEquals(1, screen.getCursorAddress());
|
||||
|
||||
// Test Word Right from pos 1 -> jumps to start of "WORLD" (pos 9)
|
||||
inputProcessor.processWordRight();
|
||||
assertEquals(9, screen.getCursorAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAll22CompatibleMethodsAndOverloads() {
|
||||
ConnectionConfig config = new ConnectionConfig("localhost", 23, TerminalModel.IBM_3279_2, false);
|
||||
Telnet3270Client client = new Telnet3270Client(config);
|
||||
|
||||
// 1. processChar
|
||||
client.processChar('A');
|
||||
client.processChar('B', 5, false);
|
||||
|
||||
// 2. processEnter
|
||||
client.processEnter();
|
||||
|
||||
// 3. processPF
|
||||
client.processPF(3);
|
||||
|
||||
// 4. processPA
|
||||
client.processPA(1);
|
||||
|
||||
// 5. processClear
|
||||
client.processClear();
|
||||
|
||||
// 6-9. Cursor navigation
|
||||
client.processCursorUp();
|
||||
client.processCursorDown();
|
||||
client.processCursorLeft();
|
||||
client.processCursorRight();
|
||||
|
||||
// 10-13. Field navigation
|
||||
client.processTab();
|
||||
client.processBackTab();
|
||||
client.processHome();
|
||||
client.processNewline();
|
||||
|
||||
// 14-16. Editing
|
||||
client.processDelete();
|
||||
client.processBackspace();
|
||||
client.processEraseEOF();
|
||||
client.processEraseInput();
|
||||
|
||||
// 17-19. Special orders & toggles
|
||||
client.processDup();
|
||||
client.processFieldMark();
|
||||
client.processToggleInsert();
|
||||
|
||||
// 20-22. Control / Selection
|
||||
client.processReset();
|
||||
client.processWordLeft();
|
||||
client.processWordRight();
|
||||
client.processFieldEnd();
|
||||
client.processAttn();
|
||||
client.processSysReq();
|
||||
client.processCurSel();
|
||||
client.processLightPen();
|
||||
client.processLightPen(10);
|
||||
|
||||
assertNotNull(client.getInputProcessor().buildReadModifiedInboundData());
|
||||
assertNotNull(client.getInputProcessor().buildReadBufferInboundData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendKeysMnemonicTokens() {
|
||||
screen.setFieldAttribute(0, (byte) 0x00);
|
||||
screen.setFieldAttribute(15, (byte) 0x00);
|
||||
screen.setFieldAttribute(30, (byte) FA_PROTECT);
|
||||
screen.setCursorAddress(1);
|
||||
|
||||
inputProcessor.sendKeys("123[tab]456[wordleft][fieldend][reset]");
|
||||
assertEquals('1', (char) screen.getCell(1).ucs4);
|
||||
assertEquals('2', (char) screen.getCell(2).ucs4);
|
||||
assertEquals('3', (char) screen.getCell(3).ucs4);
|
||||
assertEquals('4', (char) screen.getCell(16).ucs4);
|
||||
assertEquals('5', (char) screen.getCell(17).ucs4);
|
||||
assertEquals('6', (char) screen.getCell(18).ucs4);
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package haus.nightmare.lib3270j.screen;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.AbstractDBCSCodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.ecl.ECLField;
|
||||
import haus.nightmare.lib3270j.ecl.ECLFieldList;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
public class ScreenBufferPhase3Test {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private EbcdicTranslator translator;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrappedFieldListFirstFaNotAtZero() {
|
||||
screen.erase(false);
|
||||
// Place FA at pos 100 (unprotected)
|
||||
screen.setFieldAttribute(100, (byte) FA_PRINTABLE);
|
||||
// Place FA at pos 150 (protected) - wraps around 1919 back to 99
|
||||
screen.setFieldAttribute(150, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
|
||||
ECLFieldList list = screen.buildFieldList();
|
||||
assertEquals(2, list.getFieldCount());
|
||||
|
||||
ECLField f1 = list.getFirstField();
|
||||
assertNotNull(f1);
|
||||
assertEquals(100, f1.getStart());
|
||||
assertEquals(101, f1.getDataStart());
|
||||
assertEquals(149, f1.getEnd());
|
||||
assertEquals(49, f1.getLength());
|
||||
assertFalse(f1.isWrapped());
|
||||
assertFalse(f1.isProtected());
|
||||
|
||||
ECLField f2 = list.getNextField(f1);
|
||||
assertNotNull(f2);
|
||||
assertEquals(150, f2.getStart());
|
||||
assertEquals(151, f2.getDataStart());
|
||||
assertEquals(99, f2.getEnd());
|
||||
assertEquals(1920 - 150 - 1 + 100, f2.getLength());
|
||||
assertTrue(f2.isWrapped());
|
||||
assertTrue(f2.isProtected());
|
||||
|
||||
// Test field containment on wrapped field
|
||||
assertTrue(f2.contains(150));
|
||||
assertTrue(f2.contains(151));
|
||||
assertTrue(f2.contains(1919));
|
||||
assertTrue(f2.contains(0));
|
||||
assertTrue(f2.contains(99));
|
||||
assertFalse(f2.contains(100));
|
||||
assertFalse(f2.contains(120));
|
||||
|
||||
// Test field lookup at position
|
||||
assertEquals(150, screen.findFieldAt(50).getStart());
|
||||
assertEquals(100, screen.findFieldAt(120).getStart());
|
||||
|
||||
// Test navigation
|
||||
assertEquals(150, screen.findPrevField(120).getStart());
|
||||
assertEquals(150, screen.findNextField(120).getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleFieldScreenWrap() {
|
||||
screen.erase(false);
|
||||
screen.setFieldAttribute(50, (byte) FA_PRINTABLE);
|
||||
|
||||
ECLFieldList list = screen.buildFieldList();
|
||||
assertEquals(1, list.getFieldCount());
|
||||
|
||||
ECLField f = list.getFirstField();
|
||||
assertNotNull(f);
|
||||
assertEquals(50, f.getStart());
|
||||
assertEquals(51, f.getDataStart());
|
||||
assertEquals(49, f.getEnd());
|
||||
assertEquals(1919, f.getLength());
|
||||
assertTrue(f.isWrapped());
|
||||
assertTrue(f.contains(0));
|
||||
assertTrue(f.contains(50));
|
||||
assertTrue(f.contains(1919));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertCharSBCSAndOverflow() {
|
||||
screen.erase(false);
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.setFieldAttribute(6, (byte) (FA_PRINTABLE | FA_PROTECT)); // field of 5 chars (pos 1..5)
|
||||
|
||||
// Write 'A', 'B', 'C', 'D' at pos 1..4 (pos 5 is null)
|
||||
screen.setChar(0, 1, 'A');
|
||||
screen.setChar(0, 2, 'B');
|
||||
screen.setChar(0, 3, 'C');
|
||||
screen.setChar(0, 4, 'D');
|
||||
|
||||
// Insert 'Z' at pos 1
|
||||
boolean inserted = screen.insertChar(1, 'Z');
|
||||
assertTrue(inserted);
|
||||
assertEquals('Z', screen.getChar(0, 1));
|
||||
assertEquals('A', screen.getChar(0, 2));
|
||||
assertEquals('B', screen.getChar(0, 3));
|
||||
assertEquals('C', screen.getChar(0, 4));
|
||||
assertEquals('D', screen.getChar(0, 5));
|
||||
|
||||
// Field is now full (pos 5 is 'D'). Inserting another char should overflow and return false
|
||||
boolean overflow = screen.insertChar(1, 'X');
|
||||
assertFalse(overflow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAndShiftDBCSChar() {
|
||||
EbcdicTranslator dbcsTranslator = new EbcdicTranslator("930");
|
||||
AbstractDBCSCodePage cp = (AbstractDBCSCodePage) dbcsTranslator.getCodePage();
|
||||
cp.registerDbcsPair(0x4341, '\u6771'); // '東'
|
||||
|
||||
ScreenBuffer dbcsScreen = new ScreenBuffer(TerminalModel.IBM_3279_2, dbcsTranslator);
|
||||
dbcsScreen.erase(false);
|
||||
dbcsScreen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
dbcsScreen.setFieldAttribute(10, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
|
||||
// Insert DBCS char at pos 1
|
||||
boolean ok = dbcsScreen.insertChar(1, '\u6771');
|
||||
assertTrue(ok);
|
||||
assertEquals(0x43, dbcsScreen.getCell(1).ec & 0xFF);
|
||||
assertEquals(0x41, dbcsScreen.getCell(2).ec & 0xFF);
|
||||
assertEquals(ExtendedAttribute.CS_DBCS, dbcsScreen.getCell(1).cs);
|
||||
assertEquals(ExtendedAttribute.DB_LEFT, dbcsScreen.getCell(1).db);
|
||||
assertEquals(ExtendedAttribute.DB_RIGHT, dbcsScreen.getCell(2).db);
|
||||
assertEquals(3, dbcsScreen.getCursorAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCharDBCSAndCleanAdjacentSISO() {
|
||||
EbcdicTranslator dbcsTranslator = new EbcdicTranslator("930");
|
||||
AbstractDBCSCodePage cp = (AbstractDBCSCodePage) dbcsTranslator.getCodePage();
|
||||
cp.registerDbcsPair(0x4341, '\u6771');
|
||||
|
||||
ScreenBuffer dbcsScreen = new ScreenBuffer(TerminalModel.IBM_3279_2, dbcsTranslator);
|
||||
dbcsScreen.erase(false);
|
||||
dbcsScreen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
dbcsScreen.setFieldAttribute(10, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
|
||||
// Insert DBCS character at pos 1
|
||||
dbcsScreen.insertChar(1, '\u6771');
|
||||
// Delete DBCS character at pos 1
|
||||
boolean deleted = dbcsScreen.deleteChar(1);
|
||||
assertTrue(deleted);
|
||||
assertEquals(0, dbcsScreen.getCell(1).ec);
|
||||
assertEquals(0, dbcsScreen.getCell(2).ec);
|
||||
|
||||
// Test orphaned SO/SI cleanup on delete
|
||||
dbcsScreen.setCell(3, 0x0E); // SO
|
||||
dbcsScreen.setCell(4, 0x43); // DBCS byte 1
|
||||
dbcsScreen.setCell(5, 0x41); // DBCS byte 2
|
||||
dbcsScreen.setCell(6, 0x0F); // SI
|
||||
dbcsScreen.getCell(4).cs = ExtendedAttribute.CS_DBCS;
|
||||
dbcsScreen.getCell(4).db = ExtendedAttribute.DB_LEFT;
|
||||
dbcsScreen.getCell(5).cs = ExtendedAttribute.CS_DBCS;
|
||||
dbcsScreen.getCell(5).db = ExtendedAttribute.DB_RIGHT;
|
||||
|
||||
// Deleting the DBCS character at pos 4 pulls SI (pos 6) adjacent to SO (pos 3), triggering SISO cleanup
|
||||
dbcsScreen.deleteChar(4);
|
||||
assertEquals(0, dbcsScreen.getCell(3).ec);
|
||||
assertEquals(0, dbcsScreen.getCell(4).ec);
|
||||
assertEquals(0, dbcsScreen.getCell(5).ec);
|
||||
assertEquals(0, dbcsScreen.getCell(6).ec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntryAssistDOCModeAndTabStops() {
|
||||
screen.erase(false);
|
||||
assertFalse(screen.isEntryAssistDOCmode());
|
||||
assertFalse(screen.isEntryAssistWordWrap());
|
||||
|
||||
screen.setEntryAssistDOCmode(true);
|
||||
screen.setEntryAssistWordWrap(true);
|
||||
screen.setEntryAssistStartColumn(5);
|
||||
screen.setEntryAssistEndColumn(75);
|
||||
screen.setEntryAssistTabStops(new int[]{ 10, 20, 30, 40 });
|
||||
|
||||
assertTrue(screen.isEntryAssistDOCmode());
|
||||
assertTrue(screen.isEntryAssistWordWrap());
|
||||
assertEquals(5, screen.getEntryAssistStartColumn());
|
||||
assertEquals(75, screen.getEntryAssistEndColumn());
|
||||
assertArrayEquals(new int[]{ 10, 20, 30, 40 }, screen.getEntryAssistTabStops());
|
||||
|
||||
// Test word tab forward with tab stops
|
||||
screen.setCursorPosition(0, 0);
|
||||
screen.processWordTab(true);
|
||||
assertEquals(10, screen.getCursorCol());
|
||||
|
||||
screen.processWordTab(true);
|
||||
assertEquals(20, screen.getCursorCol());
|
||||
|
||||
// Test word tab backward
|
||||
screen.processWordTab(false);
|
||||
assertEquals(10, screen.getCursorCol());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessDeleteWord() {
|
||||
screen.erase(false);
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.setFieldAttribute(30, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
|
||||
// Write "HELLO WORLD" starting at pos 1
|
||||
String text = "HELLO WORLD";
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
screen.setChar(0, 1 + i, text.charAt(i));
|
||||
}
|
||||
|
||||
screen.setCursorAddress(1);
|
||||
screen.processDeleteWord();
|
||||
|
||||
// "HELLO " is deleted, "WORLD" is shifted left to pos 1
|
||||
assertEquals('W', screen.getChar(0, 1));
|
||||
assertEquals('O', screen.getChar(0, 2));
|
||||
assertEquals('R', screen.getChar(0, 3));
|
||||
assertEquals('L', screen.getChar(0, 4));
|
||||
assertEquals('D', screen.getChar(0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessSOSIDisplayTransformation() {
|
||||
EbcdicTranslator dbcsTranslator = new EbcdicTranslator("930");
|
||||
AbstractDBCSCodePage cp = (AbstractDBCSCodePage) dbcsTranslator.getCodePage();
|
||||
cp.registerDbcsPair(0x4341, '\u6771');
|
||||
|
||||
ScreenBuffer dbcsScreen = new ScreenBuffer(TerminalModel.IBM_3279_2, dbcsTranslator);
|
||||
dbcsScreen.erase(false);
|
||||
|
||||
// Put SO (0x0E), 0x43, 0x41, SI (0x0F) at pos 0..3
|
||||
dbcsScreen.setCell(0, 0x0E);
|
||||
dbcsScreen.setCell(1, 0x43);
|
||||
dbcsScreen.setCell(2, 0x41);
|
||||
dbcsScreen.setCell(3, 0x0F);
|
||||
|
||||
dbcsScreen.processSOSI();
|
||||
|
||||
assertEquals(ExtendedAttribute.DB_SO, dbcsScreen.getCell(0).db);
|
||||
assertEquals(ExtendedAttribute.DB_LEFT, dbcsScreen.getCell(1).db);
|
||||
assertEquals(ExtendedAttribute.DB_RIGHT, dbcsScreen.getCell(2).db);
|
||||
assertEquals(ExtendedAttribute.DB_SI, dbcsScreen.getCell(3).db);
|
||||
assertEquals('\u6771', dbcsScreen.getCell(1).ucs4);
|
||||
assertEquals('\u6771', dbcsScreen.getCell(2).ucs4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessorsAndConvenienceMethods() {
|
||||
screen.erase(false);
|
||||
assertEquals(24 * 80, screen.getSize());
|
||||
|
||||
screen.writeChar(10, (byte) 0xC1); // 'A' in CP037
|
||||
assertEquals('A', screen.getChar(0, 10));
|
||||
|
||||
screen.setChar(1, 5, 'Z');
|
||||
assertEquals('Z', screen.getChar(1, 5));
|
||||
|
||||
ExtendedAttribute ea = new ExtendedAttribute();
|
||||
ea.fg = 2; // RED
|
||||
screen.setExtAttr(1, 5, ea);
|
||||
assertEquals(2, screen.getExtAttr(1, 5).fg);
|
||||
|
||||
screen.setText("TESTING 123");
|
||||
assertEquals("TESTING 123", screen.getString(0, 11));
|
||||
assertEquals(0, screen.searchString("TESTING"));
|
||||
assertEquals(-1, screen.searchString("NOTFOUND"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user