IBM-DYNAMIC tweaking
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 1m25s

This commit is contained in:
2026-09-04 20:58:19 -04:00
parent cbb310b889
commit 74e30d1ead
18 changed files with 1374 additions and 86 deletions
@@ -0,0 +1,215 @@
package haus.nightmare.lib3270j.ecl;
import haus.nightmare.lib3270j.TerminalModel;
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
import haus.nightmare.lib3270j.input.InputProcessor;
import haus.nightmare.lib3270j.protocol.DS3270Constants;
import haus.nightmare.lib3270j.screen.ScreenBuffer;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.concurrent.atomic.AtomicInteger;
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
import static org.junit.jupiter.api.Assertions.*;
public class EntryAssistFullModeTest {
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); // 24x80
inputProcessor = new InputProcessor(screen, translator, null);
oia = new ECLOIA(screen, inputProcessor, null);
inputProcessor.setOIA(oia);
}
@Test
public void testDocModeCursorAdvanceWithoutWordWrap() {
// Document Mode ON, Word Wrap OFF
screen.setEntryAssistDOCmode(true);
screen.setEntryAssistWordWrap(false);
screen.setLeftMargin(0); // Col 1 (0-based 0)
screen.setRightMargin(72); // Col 73 (0-based 72)
assertTrue(screen.isEntryAssistDOCmode());
assertFalse(screen.isEntryAssistWordWrap());
assertEquals(0, screen.getEntryAssistStartColumn());
assertEquals(72, screen.getEntryAssistEndColumn());
// Setup open cursor position
screen.setCursorPosition(0, 72); // Row 0, Col 72 (at right margin)
inputProcessor.typeCharacter('X');
// Should advance to next row at left margin (Row 1, Col 0)
assertEquals(1, screen.getCursorRow());
assertEquals(0, screen.getCursorCol());
assertEquals('X', (char) screen.getCell(72).ucs4);
}
@Test
public void testWordWrapTransfersPartialWord() {
// Document Mode ON, Word Wrap ON
screen.setEntryAssistDOCmode(true);
screen.setEntryAssistWordWrap(true);
screen.setLeftMargin(5); // Start col = 5
screen.setRightMargin(20); // End col = 20
// Type "HELLO " starting at col 10
screen.setCursorPosition(0, 10);
for (char c : "HELLO ".toCharArray()) {
inputProcessor.typeCharacter(c);
}
// Now cursor is at col 16. Type "TEST" which crosses col 20
screen.setCursorPosition(0, 18);
inputProcessor.typeCharacter('W');
inputProcessor.typeCharacter('O');
inputProcessor.typeCharacter('R');
inputProcessor.typeCharacter('D'); // At col 21, past end col 20
// Word wrap should have moved "WORD" to next line starting at left margin 5
assertEquals(1, screen.getCursorRow());
assertTrue(screen.getCursorCol() >= 5);
}
@Test
public void testDocModeTabStopsNavigation() {
screen.setEntryAssistDOCmode(true);
screen.setLeftMargin(0);
screen.setRightMargin(79);
screen.setEntryAssistTabStops(new int[]{0, 10, 20, 30});
screen.setCursorPosition(0, 0);
// Word tab forward
screen.processWordTab(true);
assertEquals(10, screen.getCursorCol());
assertEquals(0, screen.getCursorRow());
screen.processWordTab(true);
assertEquals(20, screen.getCursorCol());
// Word back tab
screen.processWordTab(false);
assertEquals(10, screen.getCursorCol());
screen.processWordTab(false);
assertEquals(0, screen.getCursorCol());
}
@Test
public void testAudibleEndOfLineBell() {
inputProcessor.setBellEnabled(true);
inputProcessor.setBellColumn(74);
AtomicInteger bellCount = new AtomicInteger(0);
inputProcessor.setBellListener(bellCount::incrementAndGet);
screen.setCursorPosition(0, 70);
inputProcessor.typeCharacter('A'); // 71
inputProcessor.typeCharacter('B'); // 72
inputProcessor.typeCharacter('C'); // 73
assertEquals(0, bellCount.get());
inputProcessor.typeCharacter('D'); // 74 -> triggers bell
assertEquals(1, bellCount.get());
inputProcessor.typeCharacter('E'); // 75 -> does not retrigger on same line
assertEquals(1, bellCount.get());
// Move to next line at col 73 and type across bell column
screen.setCursorPosition(1, 73);
inputProcessor.typeCharacter('Z'); // advances to 74 -> triggers bell on next line
assertEquals(2, bellCount.get());
}
@Test
public void testInsertModeResetOnAid() {
inputProcessor.setInsertOffOnAid(true);
inputProcessor.setInsertMode(true);
assertTrue(inputProcessor.isInsertMode());
// Send AID_ENTER
inputProcessor.sendAid(AID_ENTER);
assertFalse(inputProcessor.isInsertMode(), "Insert mode should be reset after AID key");
// When insertOffOnAid is disabled
inputProcessor.setInsertOffOnAid(false);
inputProcessor.setInsertMode(true);
inputProcessor.sendAid(AID_ENTER);
assertTrue(inputProcessor.isInsertMode(), "Insert mode should be preserved when insertOffOnAid is false");
}
@Test
public void testAplKeyboardModeAndOia() {
inputProcessor.setAplKeyboardMode(true);
assertTrue(inputProcessor.isAplKeyboardMode());
// OIA status flag includes STATE_APL
long statusEx = oia.GetStatusFlagsEx();
assertEquals(ECLOIA.STATE_APL, statusEx & ECLOIA.STATE_APL);
assertTrue(oia.isApl());
// Test APL translation for key 'a' (alpha -> 0x41 with CS_GE)
screen.setCursorPosition(0, 0);
inputProcessor.typeCharacter('a');
var cell = screen.getCell(0);
assertEquals(CS_GE, cell.cs, "APL character must be stored with CS_GE (Graphic Escape) charset");
// Toggle APL mode off
inputProcessor.toggleAplKeyboardMode();
assertFalse(inputProcessor.isAplKeyboardMode());
assertFalse(oia.isApl());
assertEquals(0, oia.GetStatusFlagsEx() & ECLOIA.STATE_APL);
}
@Test
public void testNumericFieldLockSetting() {
// Field: pos 0 is numeric FA, pos 1..5 unprotected, pos 6 protect FA
screen.setFieldAttribute(0, (byte) FA_NUMERIC);
screen.setFieldAttribute(6, (byte) FA_PROTECT);
screen.setCursorAddress(1);
// Case 1: NumericFieldLock is true
inputProcessor.setNumericFieldLock(true);
inputProcessor.typeCharacter('X'); // invalid numeric character
assertTrue(inputProcessor.isKeyboardLocked(), "Keyboard should be locked on non-numeric char");
assertEquals(ECLConstants.INHIBIT_NUMERIC_ONLY, oia.getInputInhibited());
// Reset keyboard
inputProcessor.reset();
assertFalse(inputProcessor.isKeyboardLocked());
// Case 2: NumericFieldLock is false
inputProcessor.setNumericFieldLock(false);
screen.setCursorAddress(1);
inputProcessor.typeCharacter('X');
assertFalse(inputProcessor.isKeyboardLocked(), "Keyboard should NOT be locked when NumericFieldLock is false");
}
@Test
public void testOiaModeIndicators() {
screen.setEntryAssistDOCmode(true);
screen.setEntryAssistWordWrap(true);
inputProcessor.setAplKeyboardMode(true);
inputProcessor.setInsertMode(true);
long flags = oia.GetStatusFlagsEx();
assertTrue((flags & ECLOIA.STATE_DOC_MODE) != 0);
assertTrue((flags & ECLOIA.STATE_WORDWRAP) != 0);
assertTrue((flags & ECLOIA.STATE_APL) != 0);
assertTrue((flags & ECLOIA.STATE_INSERT) != 0);
assertTrue(oia.isDocMode());
assertTrue(oia.isWordWrap());
assertTrue(oia.isApl());
assertTrue(oia.isInsertMode());
}
}
@@ -76,8 +76,9 @@ public class DynamicScreenBufferTest {
assertTrue(model.isDynamic());
assertEquals(0, model.getModelNumber());
assertTrue(model.isColor());
assertEquals("IBM-DYNAMIC-E", model.getTerminalType());
assertEquals("IBM-DYNAMIC", model.getTerminalType());
assertEquals("IBM-DYNAMIC", model.getBaseTerminalType());
assertEquals("IBM-DYNAMIC", model.toString());
ScreenBuffer buffer = new ScreenBuffer(model, translator);
assertEquals(24, buffer.getDefRows());
@@ -65,12 +65,14 @@ public class DynamicTelnetFSMTest {
// Host requests TTYPE: IAC SB TTYPE SEND IAC SE
feedBytes(255, 250, 24, 1, 255, 240);
// Verify sent response is IBM-DYNAMIC-E
// Verify sent response is IBM-DYNAMIC
assertFalse(connection.sentData.isEmpty());
byte[] lastSent = connection.sentData.get(connection.sentData.size() - 1);
String s = new String(lastSent);
assertTrue(s.contains("IBM-DYNAMIC-E") || s.contains("IBM-DYNAMIC"),
assertTrue(s.contains("IBM-DYNAMIC"),
"Expected terminal type negotiation to send IBM-DYNAMIC, got: " + s);
assertFalse(s.contains("IBM-DYNAMIC-E"),
"Terminal type negotiation should not contain IBM-DYNAMIC-E, got: " + s);
}
@Test