IBM-DYNAMIC
This commit is contained in:
+475
@@ -0,0 +1,475 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Comprehensive verification for Phase 8: Codepage Converters & Adapter Bridge.
|
||||
* Verifies dynamic resolution of all 275 HoD converter class names, SBCS and DBCS conversions,
|
||||
* transparent Shift-In (0x0F) / Shift-Out (0x0E) transitions, and error handling.
|
||||
*/
|
||||
public class HODConvertersPhase8Test {
|
||||
|
||||
private static final String[] HOD_275_CONVERTER_CLASSES = new String[]{
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToChar8859_1",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp037",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp1047",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp1140",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp1146",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp1148",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp285",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp500",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharCp924",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharDBCS_ASCII",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharDBCS_EBCDIC",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharDBCS_EBCDIC_IBM",
|
||||
"com.ibm.eNetwork.HOD.converters.ByteToCharSingleByte",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByte8859_1",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp037",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp1008",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp1047",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp1140",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp1146",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp1148",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp285",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp500",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteCp924",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteDBCS_ASCII",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteDBCS_EBCDIC",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteDBCS_EBCDICN_IBM",
|
||||
"com.ibm.eNetwork.HOD.converters.CharToByteSingleByte",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT1047",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT1140",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT1146",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT1148",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT285",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT500",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterFT924",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT1090",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT1101",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT437",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT819",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT850",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT8585",
|
||||
"com.ibm.eNetwork.HOD.converters.ConverterVT923",
|
||||
"com.ibm.eNetwork.HOD.converters.HODByteToCharCp1046",
|
||||
"com.ibm.eNetwork.HOD.converters.HODByteToCharCp437",
|
||||
"com.ibm.eNetwork.HOD.converters.HODByteToCharCp850",
|
||||
"com.ibm.eNetwork.HOD.converters.HODCharToByteCp1046",
|
||||
"com.ibm.eNetwork.HOD.converters.HODCharToByteCp437",
|
||||
"com.ibm.eNetwork.HOD.converters.HODCharToByteCp850",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.ConverterBIDIPrinter420",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.ConverterFT420",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.ConverterVT1089",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.ConverterVT449",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODByteToChar8859_6",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODByteToCharCp1256",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODByteToCharCp420",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODByteToCharCp864",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODCharToByte8859_6",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODCharToByteCp1256",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODCharToByteCp420",
|
||||
"com.ibm.eNetwork.HOD.converters.ar.HODCharToByteCp864",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.ByteToCharCp1153",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.ByteToCharCp870",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.CharToByteCp1153",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.CharToByteCp870",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.ConverterFT1153",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.ConverterFT870",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.HODByteToChar8859_2",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.HODByteToCharCp852",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.HODCharToByte8859_2",
|
||||
"com.ibm.eNetwork.HOD.converters.ce.HODCharToByteCp852",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.ByteToCharCp875",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.CharToByteCp875",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.ConverterFT875",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.ConverterVT813",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.ConverterVT8586",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.HODByteToChar8859_7",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.HODByteToCharCp869",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.HODCharToByte8859_7",
|
||||
"com.ibm.eNetwork.HOD.converters.gr.HODCharToByteCp869",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ByteToCharCp424",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ByteToCharCp803",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ByteToCharCp856",
|
||||
"com.ibm.eNetwork.HOD.converters.he.CharToByteCp424",
|
||||
"com.ibm.eNetwork.HOD.converters.he.CharToByteCp803",
|
||||
"com.ibm.eNetwork.HOD.converters.he.CharToByteCp856",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterBIDIPrinter424",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterBIDIPrinter803",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterFT424",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterFT803",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterVT1134",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterVT1349",
|
||||
"com.ibm.eNetwork.HOD.converters.he.ConverterVT916",
|
||||
"com.ibm.eNetwork.HOD.converters.he.HODByteToChar8859_8",
|
||||
"com.ibm.eNetwork.HOD.converters.he.HODByteToCharCp1255",
|
||||
"com.ibm.eNetwork.HOD.converters.he.HODByteToCharCp862",
|
||||
"com.ibm.eNetwork.HOD.converters.he.HODCharToByte8859_8",
|
||||
"com.ibm.eNetwork.HOD.converters.he.HODCharToByteCp1255",
|
||||
"com.ibm.eNetwork.HOD.converters.he.HODCharToByteCp862",
|
||||
"com.ibm.eNetwork.HOD.converters.hi.ByteToCharCp1137",
|
||||
"com.ibm.eNetwork.HOD.converters.hi.CharToByteCp1137",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp1390",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp1390JIS2004",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp1399",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp1399JIS2004",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp290",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp930",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp939",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ByteToCharCp942",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp1390",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp1390JIS2004",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp1399",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp1399JIS2004",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp290",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp930",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp939",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.CharToByteCp942",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ConverterFT1390",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ConverterFT1399",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ConverterFT290",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ConverterFT930",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.ConverterFT939",
|
||||
"com.ibm.eNetwork.HOD.converters.ja.PrtConverterJIS",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.ByteToCharCp1364",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.ByteToCharCp933",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.ByteToCharCp949",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.CharToByteCp1364",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.CharToByteCp933",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.CharToByteCp949",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.ConverterFT1364",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.ConverterFT933",
|
||||
"com.ibm.eNetwork.HOD.converters.ko.PrtConverterKS25550",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp1141",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp1144",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp1145",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp1147",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp273",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp274",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp275",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp280",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp284",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp297",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp1141",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp1144",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp1145",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp1147",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp273",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp274",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp275",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp280",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp284",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.CharToByteCp297",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT1141",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT1144",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT1145",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT1147",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT273",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT274",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT275",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT280",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT284",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterFT297",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT1011",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT1012",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT1020",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT1021",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT1023",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT1104",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT220",
|
||||
"com.ibm.eNetwork.HOD.converters.onea.ConverterVT860",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ByteToCharCp1142",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ByteToCharCp1143",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ByteToCharCp1149",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ByteToCharCp277",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ByteToCharCp278",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ByteToCharCp871",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.CharToByteCp1142",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.CharToByteCp1143",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.CharToByteCp1149",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.CharToByteCp277",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.CharToByteCp278",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.CharToByteCp871",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterFT1142",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterFT1143",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterFT1149",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterFT277",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterFT278",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterFT871",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterVT1102",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterVT1103",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterVT1105",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterVT1106",
|
||||
"com.ibm.eNetwork.HOD.converters.oneb.ConverterVT865",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1025",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1112",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1122",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1123",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1154",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1156",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1157",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1158",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ByteToCharCp1166",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1025",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1112",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1122",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1123",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1154",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1156",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1157",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1158",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.CharToByteCp1166",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1025",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1112",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1122",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1123",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1154",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1156",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1157",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1158",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.ConverterFT1166",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.HODByteToChar8859_5",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.HODByteToCharCp855",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.HODByteToCharCp866",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.HODCharToByte8859_5",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.HODCharToByteCp855",
|
||||
"com.ibm.eNetwork.HOD.converters.ru.HODCharToByteCp866",
|
||||
"com.ibm.eNetwork.HOD.converters.th.ByteToCharCp1160",
|
||||
"com.ibm.eNetwork.HOD.converters.th.ByteToCharCp838",
|
||||
"com.ibm.eNetwork.HOD.converters.th.ByteToCharCp874",
|
||||
"com.ibm.eNetwork.HOD.converters.th.CharToByteCp1160",
|
||||
"com.ibm.eNetwork.HOD.converters.th.CharToByteCp838",
|
||||
"com.ibm.eNetwork.HOD.converters.th.CharToByteCp874",
|
||||
"com.ibm.eNetwork.HOD.converters.th.ConverterFT1160",
|
||||
"com.ibm.eNetwork.HOD.converters.th.ConverterFT838",
|
||||
"com.ibm.eNetwork.HOD.converters.th.ConverterVT874",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.ByteToCharCp1026",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.ByteToCharCp1155",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.CharToByteCp1026",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.CharToByteCp1155",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.ConverterFT1026",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.ConverterFT1155",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.HODByteToChar8859_9",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.HODByteToCharCp857",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.HODCharToByte8859_9",
|
||||
"com.ibm.eNetwork.HOD.converters.tr.HODCharToByteCp857",
|
||||
"com.ibm.eNetwork.HOD.converters.utf.ByteToCharUTF8",
|
||||
"com.ibm.eNetwork.HOD.converters.utf.CharToByteUTF8",
|
||||
"com.ibm.eNetwork.HOD.converters.vt.ConverterJDKEncodings",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.ByteToCharCp1381",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.ByteToCharCp1388",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.ByteToCharCp935",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.CharToByteCp1381",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.CharToByteCp1388",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.CharToByteCp935",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.ConverterFT1388",
|
||||
"com.ibm.eNetwork.HOD.converters.zh.ConverterFT935",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp1371",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp1379",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp937",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp937Macau",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp948",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp950",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ByteToCharCp964",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp1371",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp1379",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp937",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp937Macau",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp948",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp950",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteCp964",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.CharToByteEUC",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ConverterFT1371",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ConverterFT1379",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.ConverterFT937",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.PrtConverterBIG5550",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.PrtConverterCNS",
|
||||
"com.ibm.eNetwork.HOD.converters.zh_TW.PrtConverterTCA",
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testAll275HoDConverterClassNamesResolve() throws Exception {
|
||||
assertEquals(275, HOD_275_CONVERTER_CLASSES.length, "Must cover exactly 275 HoD converter class names");
|
||||
|
||||
for (String className : HOD_275_CONVERTER_CLASSES) {
|
||||
// 1. Test resolving via HODByteToCharConverter
|
||||
HODByteToCharConverter btc = HODByteToCharConverter.getHODConverter(className);
|
||||
assertNotNull(btc, "ByteToChar converter should not be null for " + className);
|
||||
assertNotNull(btc.getCodePage(), "Underlying CodePage should not be null for " + className);
|
||||
|
||||
// 2. Test resolving via HODCharToByteConverter
|
||||
HODCharToByteConverter ctb = HODCharToByteConverter.getHODConverter(className);
|
||||
assertNotNull(ctb, "CharToByte converter should not be null for " + className);
|
||||
assertNotNull(ctb.getCodePage(), "Underlying CodePage should not be null for " + className);
|
||||
|
||||
// 3. Test simple name without package qualifier
|
||||
int lastDot = className.lastIndexOf('.');
|
||||
String simpleName = className.substring(lastDot + 1);
|
||||
HODByteToCharConverter btcSimple = HODByteToCharConverter.getHODConverter(simpleName);
|
||||
assertNotNull(btcSimple, "ByteToChar converter should resolve simple name: " + simpleName);
|
||||
|
||||
// 4. Test CodePageRegistry query
|
||||
assertTrue(CodePageRegistry.hasCodePage(className), "Registry should report hasCodePage for " + className);
|
||||
assertTrue(CodePageRegistry.hasCodePage(simpleName), "Registry should report hasCodePage for " + simpleName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSbcsRoundTripConversion() throws Exception {
|
||||
String[] testEncodings = new String[]{"Cp037", "Cp1047", "Cp500", "Cp273", "Cp285", "Cp297", "Cp420", "Cp424", "Cp838", "Cp1025", "Cp1155"};
|
||||
String sampleText = "HELLO World 12345 !?";
|
||||
|
||||
for (String enc : testEncodings) {
|
||||
HODCharToByteConverter ctb = HODCharToByteConverter.getHODConverter(enc);
|
||||
HODByteToCharConverter btc = HODByteToCharConverter.getHODConverter(enc);
|
||||
|
||||
byte[] bytes = ctb.hodConvertAll(sampleText.toCharArray());
|
||||
assertNotNull(bytes);
|
||||
assertTrue(bytes.length > 0);
|
||||
|
||||
char[] chars = btc.hodConvertAll(bytes);
|
||||
assertNotNull(chars);
|
||||
assertEquals(sampleText, new String(chars), "Round-trip conversion failed for " + enc);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbcsTransparentShiftInOutTransitions() throws Exception {
|
||||
// Test Japanese DBCS Cp930
|
||||
HODByteToCharConverter btc = HODByteToCharConverter.getHODConverter("Cp930");
|
||||
HODCharToByteConverter ctb = HODCharToByteConverter.getHODConverter("Cp930");
|
||||
|
||||
assertTrue(btc.getCodePage().isDBCS());
|
||||
assertTrue(ctb.getCodePage().isDBCS());
|
||||
|
||||
// Test ideographic space \u3000 (encoded as DBCS 0x4040 in EBCDIC)
|
||||
String mixedText = "ABC\u3000XYZ";
|
||||
byte[] ebcdicBytes = ctb.hodConvertAll(mixedText.toCharArray());
|
||||
assertNotNull(ebcdicBytes);
|
||||
|
||||
// Verify that SO (0x0E) and SI (0x0F) were injected into the byte stream
|
||||
boolean hasSO = false;
|
||||
boolean hasSI = false;
|
||||
for (byte b : ebcdicBytes) {
|
||||
if (b == 0x0E) hasSO = true;
|
||||
if (b == 0x0F) hasSI = true;
|
||||
}
|
||||
assertTrue(hasSO, "CharToByte should automatically inject Shift-Out (0x0E) before DBCS characters");
|
||||
assertTrue(hasSI, "CharToByte should automatically inject Shift-In (0x0F) after DBCS characters");
|
||||
|
||||
// Decode back and verify exact text restoration
|
||||
char[] decoded = btc.hodConvertAll(ebcdicBytes);
|
||||
assertEquals(mixedText, new String(decoded), "DBCS roundtrip should restore original string without SO/SI in text");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbcsChunkedConversionAcrossBuffers() throws Exception {
|
||||
HODByteToCharConverter btc = HODByteToCharConverter.getHODConverter("Cp930");
|
||||
|
||||
// Construct a stream: 'A' (0xC1), SO (0x0E), DBCS pair (0x40, 0x40 = \u3000), SI (0x0F), 'B' (0xC2)
|
||||
// Split chunk 1 right between the 2 DBCS bytes: 0xC1, 0x0E, 0x40
|
||||
// Chunk 2: 0x40, 0x0F, 0xC2
|
||||
byte[] chunk1 = new byte[]{(byte) 0xC1, (byte) 0x0E, (byte) 0x40};
|
||||
byte[] chunk2 = new byte[]{(byte) 0x40, (byte) 0x0F, (byte) 0xC2};
|
||||
|
||||
char[] out1 = new char[10];
|
||||
char[] out2 = new char[10];
|
||||
|
||||
btc.reset();
|
||||
int n1 = btc.convert(chunk1, 0, chunk1.length, out1, 0, out1.length);
|
||||
assertEquals(1, n1, "Chunk 1 should only produce 'A' because DBCS pair is split across chunks");
|
||||
assertEquals('A', out1[0]);
|
||||
|
||||
int n2 = btc.convert(chunk2, 0, chunk2.length, out2, 0, out2.length);
|
||||
assertEquals(2, n2, "Chunk 2 should complete '\u3000' and 'B'");
|
||||
assertEquals('\u3000', out2[0]);
|
||||
assertEquals('B', out2[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbcsFlushAutoClosesDBCSShift() throws Exception {
|
||||
HODCharToByteConverter ctb = HODCharToByteConverter.getHODConverter("Cp930");
|
||||
ctb.reset();
|
||||
|
||||
char[] in = new char[]{'\u3000'}; // Only DBCS character
|
||||
byte[] out = new byte[10];
|
||||
int written = ctb.convert(in, 0, in.length, out, 0, out.length);
|
||||
assertEquals(3, written, "Should emit SO + 2-byte DBCS");
|
||||
assertEquals(0x0E, out[0]);
|
||||
assertEquals(0x40, out[1]);
|
||||
assertEquals(0x40, out[2]);
|
||||
|
||||
int flushed = ctb.flush(out, written, out.length);
|
||||
assertEquals(1, flushed, "Flush should emit Shift-In (0x0F)");
|
||||
assertEquals(0x0F, out[written]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubstitutionAndErrorHandling() throws Exception {
|
||||
HODByteToCharConverter btc = HODByteToCharConverter.getHODConverter("Cp037");
|
||||
|
||||
// Buffer overflow check
|
||||
byte[] in = new byte[]{(byte) 0xC1, (byte) 0xC2, (byte) 0xC3};
|
||||
char[] smallOut = new char[2];
|
||||
assertThrows(HODCharConversionException.class, () -> {
|
||||
btc.convert(in, 0, in.length, smallOut, 0, smallOut.length);
|
||||
});
|
||||
|
||||
// Substitution mode check on CharToByte with unmappable character
|
||||
HODCharToByteConverter ctb = HODCharToByteConverter.getHODConverter("Cp037");
|
||||
ctb.setSubstitutionMode(false);
|
||||
assertThrows(HODCharConversionException.class, () -> {
|
||||
ctb.hodConvertAll(new char[]{'\u9999'});
|
||||
});
|
||||
|
||||
ctb.setSubstitutionMode(true);
|
||||
ctb.setSubstitutionBytes(new byte[]{(byte) 0x6F});
|
||||
byte[] substituted = ctb.hodConvertAll(new char[]{'\u9999'});
|
||||
assertEquals(1, substituted.length);
|
||||
assertEquals(0x6F, substituted[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnsupportedCodepageExceptionThrown() {
|
||||
assertThrows(HODUnsupportedCodepageException.class, () -> {
|
||||
HODByteToCharConverter.getHODConverter("NonExistent_Codepage_12345");
|
||||
});
|
||||
|
||||
assertThrows(HODUnsupportedCodepageException.class, () -> {
|
||||
HODCharToByteConverter.getHODConverter("NonExistent_Codepage_12345");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCodePageGetConverterHelpers() throws Exception {
|
||||
CodePage cp037 = CodePageRegistry.getCodePage("037");
|
||||
HODByteToCharConverter btc = cp037.getByteToCharConverter();
|
||||
HODCharToByteConverter ctb = cp037.getCharToByteConverter();
|
||||
|
||||
assertNotNull(btc);
|
||||
assertNotNull(ctb);
|
||||
assertEquals("Cp037", btc.getCharacterEncoding());
|
||||
assertEquals("Cp037", ctb.getCharacterEncoding());
|
||||
|
||||
// Test static helpers on CodePage
|
||||
assertTrue(CodePage.isSurrogate('\uD800', '\uDC00'));
|
||||
assertTrue(CodePage.isHighSurrogate('\uD800'));
|
||||
assertTrue(CodePage.isLowSurrogate('\uDC00'));
|
||||
assertFalse(CodePage.isHighSurrogate('A'));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testENetworkFacades() throws Exception {
|
||||
haus.nightmare.lib3270j.converters.HODByteToCharConverter btc =
|
||||
haus.nightmare.lib3270j.eNetwork.HOD.common.HODByteToCharConverter.getHODConverter("Cp037");
|
||||
assertNotNull(btc);
|
||||
|
||||
haus.nightmare.lib3270j.converters.HODCharToByteConverter ctb =
|
||||
haus.nightmare.lib3270j.eNetwork.HOD.common.HODCharToByteConverter.getHODConverter("Cp037");
|
||||
assertNotNull(ctb);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package haus.nightmare.lib3270j.datastream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.protocol.DS3270Constants;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
public class Dynamic14BitAddressingTest {
|
||||
|
||||
private final EbcdicTranslator translator = new EbcdicTranslator("037");
|
||||
|
||||
@Test
|
||||
public void test14BitAddressEncodingAndDecoding() {
|
||||
int rows = 62;
|
||||
int cols = 160;
|
||||
int totalPositions = rows * cols; // 9920
|
||||
|
||||
// Test addresses > 4096 (which require 14-bit addressing)
|
||||
int[] testAddresses = { 4096, 4097, 5000, 7500, 9000, totalPositions - 1 };
|
||||
|
||||
for (int addr : testAddresses) {
|
||||
byte[] encoded = DS3270Constants.encodeAddress(addr, rows, cols);
|
||||
assertNotNull(encoded);
|
||||
assertEquals(2, encoded.length);
|
||||
|
||||
// In 14-bit addressing, top 2 bits of the first byte must be 00
|
||||
assertEquals(0x00, encoded[0] & 0xC0, "Top 2 bits of 14-bit address byte 1 must be 00 for addr=" + addr);
|
||||
|
||||
int decoded = DS3270Constants.decodeAddress(encoded[0] & 0xFF, encoded[1] & 0xFF);
|
||||
assertEquals(addr, decoded, "Decoded address must match original for addr=" + addr);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataStreamProcessorWith14BitSbaOrders() {
|
||||
ScreenBuffer screen = new ScreenBuffer(24, 80, 62, 160, translator);
|
||||
DataStreamProcessor ds = new DataStreamProcessor(screen, translator);
|
||||
|
||||
// Erase Write Alternate command to switch to 62x160
|
||||
// Record: [CMD_EWA, WCC, ORDER_SBA, b1, b2, 'H', 'E', 'L', 'L', 'O']
|
||||
int targetAddr = 50 * 160 + 20; // 8020 (> 4096)
|
||||
byte[] sbaAddr = DS3270Constants.encodeAddress(targetAddr, 62, 160);
|
||||
|
||||
byte[] record = new byte[] {
|
||||
(byte) CMD_EWA,
|
||||
(byte) 0xC3, // WCC
|
||||
(byte) ORDER_SBA,
|
||||
sbaAddr[0],
|
||||
sbaAddr[1],
|
||||
(byte) 0xC8, // 'H'
|
||||
(byte) 0xC5, // 'E'
|
||||
(byte) 0xD3, // 'L'
|
||||
(byte) 0xD3, // 'L'
|
||||
(byte) 0xD6 // 'O'
|
||||
};
|
||||
|
||||
ds.processRecord(record, 0, record.length, true);
|
||||
|
||||
assertTrue(screen.isScreenAlt());
|
||||
assertEquals(62, screen.getRows());
|
||||
assertEquals(160, screen.getCols());
|
||||
|
||||
// Verify characters written at target address 8020..8024
|
||||
assertEquals((byte) 0xC8, screen.getCell(targetAddr).ec); // 'H'
|
||||
assertEquals((byte) 0xC5, screen.getCell(targetAddr + 1).ec); // 'E'
|
||||
assertEquals((byte) 0xD3, screen.getCell(targetAddr + 2).ec); // 'L'
|
||||
assertEquals((byte) 0xD3, screen.getCell(targetAddr + 3).ec); // 'L'
|
||||
assertEquals((byte) 0xD6, screen.getCell(targetAddr + 4).ec); // 'O'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package haus.nightmare.lib3270j.datastream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class DynamicQueryReplyTest {
|
||||
|
||||
private final EbcdicTranslator translator = new EbcdicTranslator("037");
|
||||
|
||||
@Test
|
||||
public void testUsableAreaDynamicDimensions() {
|
||||
ScreenBuffer screen = new ScreenBuffer(24, 80, 62, 160, translator);
|
||||
QueryReplyBuilder qrBuilder = new QueryReplyBuilder(screen);
|
||||
|
||||
byte[] usableArea = qrBuilder.buildUsableArea();
|
||||
assertNotNull(usableArea);
|
||||
assertTrue(usableArea.length >= 19);
|
||||
|
||||
// Byte 0: Flags (0x01 or 0x03) - 12/14-bit addressing flag
|
||||
assertEquals(0x01, usableArea[0] & 0x01);
|
||||
|
||||
// Usable width (cols): bytes 2-3 (big endian)
|
||||
int width = ((usableArea[2] & 0xFF) << 8) | (usableArea[3] & 0xFF);
|
||||
assertEquals(160, width);
|
||||
|
||||
// Usable height (rows): bytes 4-5 (big endian)
|
||||
int height = ((usableArea[4] & 0xFF) << 8) | (usableArea[5] & 0xFF);
|
||||
assertEquals(62, height);
|
||||
|
||||
// Buffer size: last 2 bytes (bytes 17-18)
|
||||
int bufSize = ((usableArea[17] & 0xFF) << 8) | (usableArea[18] & 0xFF);
|
||||
assertEquals(160 * 62, bufSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitPartitionDynamicDimensions() {
|
||||
ScreenBuffer screen = new ScreenBuffer(24, 80, 62, 160, translator);
|
||||
QueryReplyBuilder qrBuilder = new QueryReplyBuilder(screen);
|
||||
|
||||
byte[] impPart = qrBuilder.buildImplicitPartition();
|
||||
assertNotNull(impPart);
|
||||
assertEquals(13, impPart.length);
|
||||
|
||||
// Default screen size (SDP 1): 80x24
|
||||
int defCols = ((impPart[5] & 0xFF) << 8) | (impPart[6] & 0xFF);
|
||||
int defRows = ((impPart[7] & 0xFF) << 8) | (impPart[8] & 0xFF);
|
||||
assertEquals(80, defCols);
|
||||
assertEquals(24, defRows);
|
||||
|
||||
// Alternate screen size: 160x62
|
||||
int altCols = ((impPart[9] & 0xFF) << 8) | (impPart[10] & 0xFF);
|
||||
int altRows = ((impPart[11] & 0xFF) << 8) | (impPart[12] & 0xFF);
|
||||
assertEquals(160, altCols);
|
||||
assertEquals(62, altRows);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlphaPartitionsDynamicStorage() {
|
||||
ScreenBuffer screen = new ScreenBuffer(24, 80, 62, 160, translator);
|
||||
QueryReplyBuilder qrBuilder = new QueryReplyBuilder(screen);
|
||||
|
||||
byte[] alphaPart = qrBuilder.buildAlphaPartitions();
|
||||
assertNotNull(alphaPart);
|
||||
assertEquals(4, alphaPart.length);
|
||||
|
||||
// Total partition storage: bytes 1-2
|
||||
int storage = ((alphaPart[1] & 0xFF) << 8) | (alphaPart[2] & 0xFF);
|
||||
assertEquals(160 * 62, storage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
package haus.nightmare.lib3270j.ecl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Comprehensive test suite for Phase 4: Canonical Package Exposure & ECL Core API Expansion.
|
||||
*/
|
||||
public class Phase4EclCoreTest {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private InputProcessor inputProcessor;
|
||||
private EbcdicTranslator translator;
|
||||
private ECLPS ps;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
translator = new EbcdicTranslator("037");
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||
inputProcessor = new InputProcessor(screen, translator, null);
|
||||
ps = new ECLPS(screen, inputProcessor, translator);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4.1 ECLSession Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testSessionMetadataAndProperties() {
|
||||
ECLSession session = new ECLSession();
|
||||
session.setSessionName("B");
|
||||
assertEquals("B", session.getSessionName());
|
||||
assertEquals("B", session.GetSessionName());
|
||||
|
||||
session.setSessionLabel("Mainframe Prod");
|
||||
assertEquals("Mainframe Prod", session.getSessionLabel());
|
||||
assertEquals("Mainframe Prod", session.GetSessionLabel());
|
||||
|
||||
session.setAssociatedDeviceName("DEV3270A");
|
||||
assertEquals("DEV3270A", session.getAssociatedDeviceName());
|
||||
assertEquals("DEV3270A", session.GetAssociatedDeviceName());
|
||||
|
||||
session.setSessionCounter(5);
|
||||
assertEquals(5, session.getSessionCount());
|
||||
assertEquals(5, session.GetSessionCount());
|
||||
|
||||
session.setMacroID(42);
|
||||
assertEquals(42, session.getMacroID());
|
||||
assertEquals(42, session.GetMacroID());
|
||||
|
||||
session.setKeyStrength(256);
|
||||
assertEquals(256, session.getKeyStrength());
|
||||
assertEquals(256, session.GetKeyStrength());
|
||||
|
||||
ClassLoader cl = getClass().getClassLoader();
|
||||
session.SetCustomizedCAsClassLoader(cl);
|
||||
assertSame(cl, session.GetCustomizedCAsClassLoader());
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty("USER", "SYSADMIN");
|
||||
session.sendNewEnvironmentVariables(props);
|
||||
assertEquals("SYSADMIN", session.getProperties().getProperty("USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartCommunicationWithBlockingDescriptor() throws Exception {
|
||||
ECLSession session = new ECLSession();
|
||||
// Matching screen descriptor with string array convenience overload
|
||||
String[] patterns = new String[] { "WELCOME", "SIGNON" };
|
||||
assertNotNull(patterns);
|
||||
|
||||
// Verify ECLErr on unmatched descriptor with timeout
|
||||
ECLScreenDesc descFail = new ECLScreenDesc();
|
||||
descFail.AddString("UNMATCHABLE_SCREEN_PATTERN", 1, 1);
|
||||
try {
|
||||
// timeout in seconds (1s = 1000ms)
|
||||
session.StartCommunicationWithBlocking(1, descFail);
|
||||
} catch (ECLErr | IOException expected) {
|
||||
// Expected either connection failure or descriptor timeout ECLErr
|
||||
assertNotNull(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionWaitWhileScreen() {
|
||||
ECLSession session = new ECLSession();
|
||||
ECLScreenDesc desc = new ECLScreenDesc();
|
||||
desc.AddString("NONEXISTENT", 1, 1);
|
||||
// Should return true immediately as screen does NOT match
|
||||
assertTrue(session.WaitWhileScreen(desc, 100));
|
||||
assertTrue(session.waitWhileScreen(desc, 100));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4.2 ECLPS Presentation Space Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testPSDocModeAndTabHandling() {
|
||||
// Tab mode "2" replaces \t with spaces
|
||||
String tabbed = "ABC\tDEF";
|
||||
String spaceExpanded = ps.handleTabs(tabbed, "2");
|
||||
assertEquals("ABC DEF", spaceExpanded);
|
||||
|
||||
// Tab mode "1" aligns to tab stop columns (8-column default)
|
||||
String colAligned = ps.handleTabs("A\tB", "1");
|
||||
assertEquals("A B", colAligned);
|
||||
|
||||
// Null / unmodified modes
|
||||
assertEquals("ABC", ps.handleTabs("ABC", "0"));
|
||||
assertEquals(null, ps.handleTabs(null, "1"));
|
||||
|
||||
// Trim on paste
|
||||
assertFalse(ps.enableTrimOnPaste());
|
||||
assertFalse(ps.EnableTrimOnPaste());
|
||||
|
||||
// Paste in Doc Mode (when not in DOC mode delegates to normal paste)
|
||||
int pasted = ps.pasteInDocMode("HELLO", 1, 1);
|
||||
assertEquals(5, pasted);
|
||||
assertEquals("HELLO", ps.getString(0, 0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPSCheckBeforeSendKeysAndBadgeReader() throws Exception {
|
||||
assertEquals(0, ps.CheckBeforeSendKeys("ABC"));
|
||||
assertEquals(0, ps.CheckBeforeSendKeys("ABC", 1));
|
||||
assertEquals(0, ps.CheckBeforeSendKeys("ABC", 1, 1));
|
||||
|
||||
// Badge reader writes data and sends [enter]
|
||||
ps.BadgeReader("BADGE123", 1, 1);
|
||||
assertEquals("BADGE123", ps.getString(0, 0, 8));
|
||||
|
||||
// Out of bounds badge reader throws ECLErr
|
||||
assertThrows(ECLErr.class, () -> {
|
||||
ps.BadgeReader("INVALID", 99999);
|
||||
});
|
||||
|
||||
ps.asisBadgeReader("BADGE456", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPSGetScreenRect() {
|
||||
ps.SetText("ROW1DATA", 1, 1);
|
||||
ps.SetText("ROW2DATA", 2, 1);
|
||||
|
||||
char[] rectChars = new char[16];
|
||||
int count = ps.GetScreenRect(rectChars, 16, 1, 1, 2, 8, ECLConstants.PLANE_TEXT);
|
||||
assertEquals(16, count);
|
||||
assertEquals("ROW1DATAROW2DATA", new String(rectChars));
|
||||
|
||||
String rectStr = ps.GetScreenRect(1, 1, 2, 8);
|
||||
assertTrue(rectStr.contains("ROW1DATA"));
|
||||
assertTrue(rectStr.contains("ROW2DATA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPSLanguageAndGraphicsServices() throws Exception {
|
||||
ECLPSGraphicsServices gs = ps.GetPSGraphicsServices();
|
||||
assertNotNull(gs);
|
||||
assertSame(gs, ps.GetECLPSGraphicsServices());
|
||||
assertSame(gs, ps.getGraphicsServices());
|
||||
|
||||
AtomicBoolean graphicsUpdatedFired = new AtomicBoolean(false);
|
||||
gs.addGraphicsListener(new ECLPSGraphicsListener() {
|
||||
@Override
|
||||
public void graphicsEvent(ECLPSGraphicsEvent event) {}
|
||||
@Override
|
||||
public void graphicsUpdated(ECLPSGraphicsEvent event) {
|
||||
graphicsUpdatedFired.set(true);
|
||||
}
|
||||
});
|
||||
gs.mousePressed(10, 20, 1);
|
||||
assertTrue(graphicsUpdatedFired.get());
|
||||
|
||||
// BIDI services
|
||||
ECLPSBIDIServices bidi = ps.GetPSBIDIServices();
|
||||
assertNotNull(bidi);
|
||||
bidi.SetNumeralShape(ECLPSBIDIServices.NATIONAL);
|
||||
assertEquals(ECLPSBIDIServices.NATIONAL, bidi.GetNumeralShape());
|
||||
bidi.SetTextOrientation(ECLPSBIDIServices.RIGHT_TO_LEFT);
|
||||
assertEquals(ECLPSBIDIServices.RIGHT_TO_LEFT, bidi.GetTextOrientation());
|
||||
bidi.setNumericSwap(true);
|
||||
assertTrue(bidi.getNumericSwap());
|
||||
|
||||
// Hindi services
|
||||
ECLPSHindiServices hindi = ps.GetPSHindiServices();
|
||||
assertNotNull(hindi);
|
||||
assertEquals(5, hindi.GetHindiCursorCol(1, 5));
|
||||
assertEquals(0, hindi.GetHindiCursorLevel(1, 5));
|
||||
|
||||
// Thai services
|
||||
ECLPSTHAIServices thai = ps.GetPSTHAIServices();
|
||||
assertNotNull(thai);
|
||||
thai.SetThaiDisplayMode(2);
|
||||
assertEquals(2, thai.GetThaiDisplayMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPSGetDSAndParent() {
|
||||
assertNull(ps.GetParent());
|
||||
ECLSession session = new ECLSession();
|
||||
ps.setSession(session);
|
||||
assertSame(session, ps.GetParent());
|
||||
assertSame(session, ps.getParent());
|
||||
|
||||
Object dummyHist = new Object();
|
||||
ps.setScreenHistory(dummyHist);
|
||||
assertSame(dummyHist, ps.getScreenHistory());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4.3 ECLOIA Operator Information Area Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testOIAStatusFlagsAndBitmasks() {
|
||||
ECLOIA oia = new ECLOIA(screen, inputProcessor, null);
|
||||
|
||||
long flagsEx = oia.GetStatusFlagsEx();
|
||||
// Online and controller ready are set by default
|
||||
assertEquals(ECLOIA.STATE_ONLINE, flagsEx & ECLOIA.STATE_ONLINE);
|
||||
assertEquals(ECLOIA.STATE_CONTROLLER_READY, flagsEx & ECLOIA.STATE_CONTROLLER_READY);
|
||||
|
||||
// Toggle insert mode and verify status flag reflection
|
||||
inputProcessor.setInsertMode(true);
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_INSERT) != 0);
|
||||
|
||||
// DOC Mode & WordWrap
|
||||
screen.setEntryAssistDOCmode(true);
|
||||
screen.setEntryAssistWordWrap(true);
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_DOC_MODE) != 0);
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_WORDWRAP) != 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOIADoNotEnterAndReadyConnect() {
|
||||
ECLOIA oia = new ECLOIA(screen, inputProcessor, null);
|
||||
|
||||
// Set do not enter group 8 (SYS LOCK)
|
||||
oia.setDoNotEnter(8, 0);
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_SYS_LOCK) != 0);
|
||||
|
||||
// Clear do not enter
|
||||
oia.clearDoNotEnter();
|
||||
assertEquals(0, oia.GetStatusFlagsEx() & ECLOIA.STATE_SYS_LOCK);
|
||||
|
||||
// ReadyConnect state
|
||||
oia.setReadyConnect(4, "MYJOB");
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_MY_JOB) != 0);
|
||||
|
||||
// Msg waiting and OIA invisible
|
||||
oia.setMsgWaiting(true);
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_MSG_WAITING) != 0);
|
||||
|
||||
oia.setOiaInvisible();
|
||||
assertTrue((oia.GetStatusFlagsEx() & ECLOIA.STATE_OIA_SUPPRESS) != 0);
|
||||
|
||||
oia.writeToOIA("STATUS_MSG");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOIALanguageServices() {
|
||||
ECLOIA oia = new ECLOIA(screen, inputProcessor, null);
|
||||
assertNotNull(oia.GetECLOIABIDI());
|
||||
assertNotNull(oia.GetECLOIATHAI());
|
||||
assertNotNull(oia.GetECLOIAHindi());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4.4 ECLConnection JavaBean Properties Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testConnectionJavaBeanProperties() {
|
||||
ECLConnection conn = new ECLConnection();
|
||||
|
||||
conn.SetHost("mvs.example.com");
|
||||
assertEquals("mvs.example.com", conn.GetHost());
|
||||
assertEquals("mvs.example.com", conn.getHost());
|
||||
|
||||
conn.SetPort(2023);
|
||||
assertEquals(2023, conn.GetPort());
|
||||
assertEquals(2023, conn.getPort());
|
||||
|
||||
conn.SetCodePage("1047");
|
||||
assertEquals("1047", conn.GetCodePage());
|
||||
assertEquals("1047", conn.getCodePage());
|
||||
|
||||
conn.SetDeviceName("IBM-3278-2");
|
||||
assertEquals("IBM-3278-2", conn.GetDeviceName());
|
||||
|
||||
conn.SetLUName("TSO001");
|
||||
assertEquals("TSO001", conn.GetLUName());
|
||||
|
||||
conn.SetWorkstationID("WS42");
|
||||
assertEquals("WS42", conn.GetWorkstationID());
|
||||
|
||||
conn.SetSSL(true);
|
||||
assertTrue(conn.IsSSL());
|
||||
assertTrue(conn.isSSL());
|
||||
|
||||
conn.setContentionResolution(true);
|
||||
assertTrue(conn.getContentionResolution());
|
||||
assertTrue(conn.isContentionResolution());
|
||||
|
||||
conn.set_LULU_Session(true);
|
||||
assertTrue(conn.is_LULU_Session());
|
||||
|
||||
conn.setNegotiatedCResolution(true);
|
||||
assertTrue(conn.isNegotiateCResolution());
|
||||
|
||||
conn.setBIND7FArchitectureViolation(true);
|
||||
assertTrue(conn.isBIND7FArchitectureViolation());
|
||||
|
||||
conn.SetKeyRemap("/keys/remap.xml");
|
||||
assertEquals("/keys/remap.xml", conn.GetKeyRemap());
|
||||
|
||||
conn.SetCertificateName("ClientCert1");
|
||||
assertEquals("ClientCert1", conn.GetCertificateName());
|
||||
|
||||
conn.SetCertificateSource("LOCAL_STORE");
|
||||
assertEquals("LOCAL_STORE", conn.GetCertificateSource());
|
||||
|
||||
conn.SetCertificateURL("file:///certs/ca.pem");
|
||||
assertEquals("file:///certs/ca.pem", conn.GetCertificateURL());
|
||||
|
||||
conn.SetCertificatePassword("secret");
|
||||
assertEquals("secret", conn.GetCertificatePassword());
|
||||
|
||||
conn.SetCertificateProvided(true);
|
||||
assertTrue(conn.isCertificateProvided());
|
||||
|
||||
conn.SetSecurityProtocol("TLS");
|
||||
assertEquals("TLS", conn.GetSecurityProtocol());
|
||||
|
||||
conn.SetTLSProtocolVersion("TLSv1.3");
|
||||
assertEquals("TLSv1.3", conn.GetTLSProtocolVersion());
|
||||
|
||||
conn.SetUseJSSE(true);
|
||||
assertTrue(conn.isUseJSSE());
|
||||
|
||||
conn.SetJSSETrustStore("/lib/security/cacerts");
|
||||
assertEquals("/lib/security/cacerts", conn.GetJSSETrustStore());
|
||||
|
||||
conn.SetJSSETrustStoreType("PKCS12");
|
||||
assertEquals("PKCS12", conn.GetJSSETrustStoreType());
|
||||
|
||||
conn.SetJSSETrustStorePassword("changeit");
|
||||
assertEquals("changeit", conn.GetJSSETrustStorePassword());
|
||||
|
||||
conn.SetProxyType("SOCKS5");
|
||||
assertEquals("SOCKS5", conn.GetProxyType());
|
||||
|
||||
conn.SetProxyServerName("proxy.corp.com");
|
||||
assertEquals("proxy.corp.com", conn.GetProxyServerName());
|
||||
|
||||
conn.SetProxyServerPort("1080");
|
||||
assertEquals("1080", conn.GetProxyServerPort());
|
||||
|
||||
conn.SetProxyUserID("proxyUser");
|
||||
assertEquals("proxyUser", conn.GetProxyUserID());
|
||||
|
||||
conn.SetProxyUserPassword("proxyPass");
|
||||
assertEquals("proxyPass", conn.GetProxyUserPassword());
|
||||
|
||||
conn.SetProxyAuthenMethod("BASIC");
|
||||
assertEquals("BASIC", conn.GetProxyAuthenMethod());
|
||||
|
||||
conn.SetProxySecurityProtocol("TLS");
|
||||
assertEquals("TLS", conn.GetProxySecurityProtocol());
|
||||
|
||||
// convertData boolean translation
|
||||
Properties props = new Properties();
|
||||
props.setProperty("enableSSL", "true");
|
||||
props.setProperty("contentionRes", "false");
|
||||
props.setProperty("otherProp", "hello");
|
||||
conn.convertData(props);
|
||||
|
||||
assertEquals("1", props.getProperty("enableSSL"));
|
||||
assertEquals("0", props.getProperty("contentionRes"));
|
||||
assertEquals("hello", props.getProperty("otherProp"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4.5 ECLField & ECLFieldList Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testECLFieldAndFieldListAttributes() {
|
||||
// Build 2 fields: Field 1 (unprotected, modifiable) and Field 2 (protected, high intensity)
|
||||
screen.setFieldAttribute(0, (byte) (FA_PROTECT | FA_INT_HIGH_SEL)); // Pos 0: Prot, High
|
||||
screen.setFieldAttribute(40, (byte) (FA_MODIFY)); // Pos 40: Unprot, Modified
|
||||
|
||||
ECLFieldList fl = ps.getFieldList();
|
||||
assertEquals(2, fl.getFieldCount());
|
||||
|
||||
ECLField f1 = fl.findField(5);
|
||||
assertNotNull(f1);
|
||||
assertTrue(f1.IsProtected());
|
||||
assertTrue(f1.IsHighIntensity());
|
||||
assertEquals(0, f1.getStartFieldPos());
|
||||
|
||||
// Test field plane copying
|
||||
char[] planeData = f1.copyPlanes(ECLConstants.PLANE_TEXT);
|
||||
assertNotNull(planeData);
|
||||
|
||||
// Dynamically alter field attribute
|
||||
f1.setFieldAttribute((char) (FA_MODIFY | FA_NUMERIC));
|
||||
assertTrue(f1.IsModified());
|
||||
assertTrue(f1.IsNumeric());
|
||||
|
||||
// Test ECLFieldList locateField and matchAttributes
|
||||
// 0x01 = Modified, 0x20 = Protected, 0x10 = High Intensity
|
||||
ECLField matchedMod = fl.locateField(0x01, null);
|
||||
assertNotNull(matchedMod);
|
||||
|
||||
ECLField matchedNotMod = fl.locateField(0x100, null); // 0x100 = Not modified
|
||||
assertNull(matchedNotMod); // Both are modified now
|
||||
|
||||
fl.copyPlanes(ECLConstants.PLANE_TEXT);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4.6 ECLScreenDesc & ECLScreenReco Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testScreenDescCriteriaAndBufferMatching() {
|
||||
ECLScreenDesc desc = new ECLScreenDesc();
|
||||
desc.AddString("MAINFRAME", 1, 1, false);
|
||||
desc.AddCursorPos(1, 10);
|
||||
|
||||
ps.SetText("MAINFRAME", 1, 1);
|
||||
ps.setCursorPos(0, 9); // 0-indexed row 0, col 9 = 1-indexed row 1, col 10
|
||||
|
||||
assertTrue(desc.Matches(ps, null));
|
||||
assertTrue(desc.Matches(screen));
|
||||
|
||||
// Add field attribute criteria in a dedicated descriptor test
|
||||
ECLScreenDesc descFa = new ECLScreenDesc();
|
||||
screen.setFieldAttribute(0, (byte) FA_PROTECT);
|
||||
descFa.AddFieldAttr(1, 0x20); // 0x20 = Protected
|
||||
assertTrue(descFa.Matches(ps, null));
|
||||
assertTrue(descFa.Matches(screen));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScreenRecoStaticAndDynamic() {
|
||||
ps.SetText("LOGIN SCREEN", 1, 1);
|
||||
ps.SetText("PASSWORD", 5, 1);
|
||||
|
||||
ECLScreenDesc desc = new ECLScreenDesc();
|
||||
desc.AddString("LOGIN SCREEN", 1, 1, true);
|
||||
|
||||
// Static matching
|
||||
assertTrue(ECLScreenReco.IsMatch(ps, desc));
|
||||
assertTrue(ECLScreenReco.compareTextAt(ps, "LOGIN SCREEN", 1, 1, true));
|
||||
assertTrue(ECLScreenReco.compareTextInRect(ps, "PASSWORD", 5, 1, 5, 8, true));
|
||||
assertFalse(ECLScreenReco.compareTextAt(ps, "UNKNOWN", 1, 1, true));
|
||||
|
||||
// Dynamic recognition with notification callback
|
||||
ECLScreenReco reco = new ECLScreenReco(ps);
|
||||
AtomicBoolean screenMatched = new AtomicBoolean(false);
|
||||
|
||||
reco.RegisterScreen(desc, new ECLScreenNotify() {
|
||||
@Override
|
||||
public void screenMatching(ECLScreenRecoEvent event) {
|
||||
screenMatched.set(true);
|
||||
assertSame(desc, event.getScreenDesc());
|
||||
assertSame(ps, event.getPS());
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(screenMatched.get());
|
||||
|
||||
// Test unregister
|
||||
reco.UnregisterScreen(desc, (ECLScreenNotify) null);
|
||||
reco.dispose();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Drop-in Canonical eNetwork.ECL Hierarchy Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testDropInENetworkECLHierarchy() {
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.ECLSession session =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.ECLSession();
|
||||
assertNotNull(session.GetPS());
|
||||
assertNotNull(session.GetOIA());
|
||||
assertNotNull(session.GetConnection());
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.ECLScreenDesc desc =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.ECLScreenDesc();
|
||||
desc.AddString("TEST");
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.ECLScreenReco reco =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.ECLScreenReco(session);
|
||||
assertNotNull(reco);
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.ECLErr err =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.ECLErr("ECLSession", "ECL0001", "Timeout");
|
||||
assertEquals("ECLSession", err.getTag());
|
||||
assertEquals("ECL0001", err.getID());
|
||||
assertEquals("Timeout", err.getText());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
package haus.nightmare.lib3270j.ecl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionState;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Comprehensive test suite for Phase 5: ECL Event Model & Listener Signatures.
|
||||
*/
|
||||
public class Phase5EclEventTest {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private InputProcessor inputProcessor;
|
||||
private EbcdicTranslator translator;
|
||||
private ECLPS ps;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
translator = new EbcdicTranslator("037");
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||
inputProcessor = new InputProcessor(screen, translator, null);
|
||||
ps = new ECLPS(screen, inputProcessor, translator);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5.1 ECLPSEvent and ECLPSUpdate Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testECLPSEventGettersAndCoordinates() {
|
||||
// Create event with 0-based coordinates row 2, col 5 to row 4, col 10 (on 80 col screen)
|
||||
ECLPSEvent evt = new ECLPSEvent(ps, ECLPSEvent.PS_UPDATE, ECLPSEvent.HOST_EVENTS,
|
||||
2, 5, 4, 10, 100, 200, 24, 80, false, true, 42, true, null);
|
||||
|
||||
assertSame(ps, evt.getPS());
|
||||
assertSame(ps, evt.GetPS());
|
||||
assertEquals(ECLPSEvent.HOST_EVENTS, evt.GetType());
|
||||
assertEquals(ECLPSEvent.HOST_EVENTS, evt.getType());
|
||||
assertEquals(ECLPSEvent.PS_UPDATE, evt.getEventType());
|
||||
|
||||
// 0-based legacy getters
|
||||
assertEquals(2, evt.getStartRow());
|
||||
assertEquals(5, evt.getStartCol());
|
||||
assertEquals(4, evt.getEndRow());
|
||||
assertEquals(10, evt.getEndCol());
|
||||
assertEquals(2 * 80 + 5, evt.getStart());
|
||||
assertEquals(4 * 80 + 10, evt.getEnd());
|
||||
|
||||
// 1-based HoD specification getters
|
||||
assertEquals(3, evt.GetStartRow());
|
||||
assertEquals(6, evt.GetStartCol());
|
||||
assertEquals(5, evt.GetEndRow());
|
||||
assertEquals(11, evt.GetEndCol());
|
||||
assertEquals(2 * 80 + 5 + 1, evt.GetStart());
|
||||
assertEquals(4 * 80 + 10 + 1, evt.GetEnd());
|
||||
|
||||
// Indicator flags and counters
|
||||
assertTrue(evt.GetCursorVisible());
|
||||
assertTrue(evt.isCursorVisible());
|
||||
assertEquals(42, evt.GetRingCounter());
|
||||
assertEquals(42, evt.getRingCounter());
|
||||
assertTrue(evt.isStartPrinterBit());
|
||||
assertTrue(evt.IsStartPrinterBit());
|
||||
assertFalse(evt.isFullUpdate());
|
||||
assertFalse(evt.IsFullUpdate());
|
||||
|
||||
// ECLPSUpdate object
|
||||
ECLPSUpdate update = evt.getECLPSUpdate();
|
||||
assertNotNull(update);
|
||||
assertSame(update, evt.GetECLPSUpdate());
|
||||
assertEquals(3, update.GetStartRow());
|
||||
assertEquals(6, update.GetStartCol());
|
||||
assertEquals(5, update.GetEndRow());
|
||||
assertEquals(11, update.GetEndCol());
|
||||
assertEquals(2 * 80 + 5 + 1, update.GetStart());
|
||||
assertEquals(4 * 80 + 10 + 1, update.GetEnd());
|
||||
assertFalse(update.isFullUpdate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testECLPSUpdateStandalone() {
|
||||
ECLPSUpdate update = new ECLPSUpdate(ps, 0, 0, 23, 79, 0, 1919, true, "FULL_SCREEN");
|
||||
assertSame(ps, update.getPS());
|
||||
assertSame(ps, update.GetPS());
|
||||
assertEquals(0, update.getStartRow());
|
||||
assertEquals(1, update.GetStartRow());
|
||||
assertEquals(0, update.getStartCol());
|
||||
assertEquals(1, update.GetStartCol());
|
||||
assertEquals(23, update.getEndRow());
|
||||
assertEquals(24, update.GetEndRow());
|
||||
assertEquals(79, update.getEndCol());
|
||||
assertEquals(80, update.GetEndCol());
|
||||
assertEquals(0, update.getStart());
|
||||
assertEquals(1, update.GetStart());
|
||||
assertEquals(1919, update.getEnd());
|
||||
assertEquals(1920, update.GetEnd());
|
||||
assertTrue(update.isFullUpdate());
|
||||
assertTrue(update.IsFullUpdate());
|
||||
assertEquals("FULL_SCREEN", update.getText());
|
||||
assertEquals("FULL_SCREEN", update.GetText());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5.2 ECLPSListener Callbacks, Filtering, and Stop/Error Notifications
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testPSNotifyEventAndFilter() {
|
||||
AtomicInteger hostEventsReceived = new AtomicInteger(0);
|
||||
AtomicInteger userEventsReceived = new AtomicInteger(0);
|
||||
AtomicReference<ECLPSEvent> lastHostEvent = new AtomicReference<>();
|
||||
|
||||
ECLPSListener hostListener = new ECLPSListener() {
|
||||
@Override
|
||||
public void PSNotifyEvent(ECLPSEvent event) {
|
||||
hostEventsReceived.incrementAndGet();
|
||||
lastHostEvent.set(event);
|
||||
}
|
||||
};
|
||||
|
||||
ECLPSListener userListener = new ECLPSListener() {
|
||||
@Override
|
||||
public void PSNotifyEvent(ECLPSEvent event) {
|
||||
userEventsReceived.incrementAndGet();
|
||||
}
|
||||
};
|
||||
|
||||
// Register host listener for HOST_EVENTS only
|
||||
ps.RegisterPSEvent(hostListener, ECLPS.HOST_EVENTS);
|
||||
// Register user listener for USER_EVENTS only
|
||||
ps.RegisterPSEvent(userListener, ECLPS.USER_EVENTS);
|
||||
|
||||
// 1. Dispatch Host update
|
||||
ps.notifyPSUpdate(1, 1, 1, 10, false, ECLPS.HOST_EVENTS, true);
|
||||
assertEquals(1, hostEventsReceived.get());
|
||||
assertEquals(0, userEventsReceived.get());
|
||||
assertNotNull(lastHostEvent.get());
|
||||
assertTrue(lastHostEvent.get().isStartPrinterBit());
|
||||
assertTrue(lastHostEvent.get().GetRingCounter() > 0);
|
||||
|
||||
// 2. Dispatch User cursor move
|
||||
ps.notifyCursorMoved(0, 50);
|
||||
assertEquals(1, hostEventsReceived.get());
|
||||
assertEquals(1, userEventsReceived.get());
|
||||
|
||||
// 3. Dispatch Alarm (HOST_EVENTS)
|
||||
ps.notifyAlarm();
|
||||
assertEquals(2, hostEventsReceived.get());
|
||||
assertEquals(1, userEventsReceived.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPSNotifyErrorAndStop() {
|
||||
AtomicReference<ECLErr> capturedErr = new AtomicReference<>();
|
||||
AtomicInteger stopReason = new AtomicInteger(0);
|
||||
|
||||
ECLPSListener listener = new ECLPSListener() {
|
||||
@Override
|
||||
public void PSNotifyEvent(ECLPSEvent event) {}
|
||||
|
||||
@Override
|
||||
public void PSNotifyError(ECLPS p, ECLErr err) {
|
||||
capturedErr.set(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void PSNotifyStop(ECLPS p, int reason) {
|
||||
stopReason.set(reason);
|
||||
}
|
||||
};
|
||||
|
||||
ps.RegisterPSEvent(listener);
|
||||
|
||||
// Fire error
|
||||
ECLErr testErr = new ECLErr("ECLPS", "ECL0099", "Synthetic test error");
|
||||
ps.notifyPSError(testErr);
|
||||
assertSame(testErr, capturedErr.get());
|
||||
|
||||
// Unregister triggers STOP_UNREGISTER
|
||||
ps.UnregisterPSEvent(listener);
|
||||
assertEquals(ECLPS.STOP_UNREGISTER, stopReason.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyPSListenerCompatibility() {
|
||||
AtomicInteger legacyChanged = new AtomicInteger(0);
|
||||
AtomicInteger legacyCursor = new AtomicInteger(0);
|
||||
AtomicInteger legacyAlarm = new AtomicInteger(0);
|
||||
AtomicInteger legacyResize = new AtomicInteger(0);
|
||||
|
||||
ECLPSListener legacyListener = new ECLPSListener() {
|
||||
@Override
|
||||
public void psChanged(ECLPSEvent event) {
|
||||
legacyChanged.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void psCursorMoved(ECLPSEvent event) {
|
||||
legacyCursor.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void psAlarm(ECLPSEvent event) {
|
||||
legacyAlarm.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void psResized(ECLPSEvent event) {
|
||||
legacyResize.incrementAndGet();
|
||||
}
|
||||
};
|
||||
|
||||
ps.RegisterPSEvent(legacyListener);
|
||||
|
||||
ps.notifyPSUpdate(0, 0, 1, 1, false);
|
||||
assertEquals(1, legacyChanged.get());
|
||||
|
||||
ps.notifyCursorMoved(0, 10);
|
||||
assertEquals(2, legacyChanged.get());
|
||||
assertEquals(1, legacyCursor.get());
|
||||
|
||||
ps.notifyAlarm();
|
||||
assertEquals(3, legacyChanged.get());
|
||||
assertEquals(1, legacyAlarm.get());
|
||||
|
||||
ps.notifyScreenResized(24, 80);
|
||||
assertEquals(4, legacyChanged.get());
|
||||
assertEquals(1, legacyResize.get());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5.3 ECLOIAEvent, ECLOIAListener, and ECLOIANotify Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testOIAEventAndListenerSignatures() {
|
||||
ECLOIA oia = new ECLOIA(screen, inputProcessor, null);
|
||||
|
||||
AtomicInteger oiaListenerEvents = new AtomicInteger(0);
|
||||
AtomicInteger oiaNotifyEvents = new AtomicInteger(0);
|
||||
AtomicReference<ECLOIAEvent> lastOiaEvent = new AtomicReference<>();
|
||||
AtomicReference<ECLErr> oiaError = new AtomicReference<>();
|
||||
AtomicInteger oiaStopReason = new AtomicInteger(0);
|
||||
|
||||
ECLOIAListener listener = new ECLOIAListener() {
|
||||
@Override
|
||||
public void OIANotifyEvent(ECLOIAEvent event) {
|
||||
oiaListenerEvents.incrementAndGet();
|
||||
lastOiaEvent.set(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void OIANotifyError(ECLOIA source, ECLErr err) {
|
||||
oiaError.set(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void OIANotifyStop(ECLOIA source, int reason) {
|
||||
oiaStopReason.set(reason);
|
||||
}
|
||||
};
|
||||
|
||||
ECLOIANotify notify = new ECLOIANotify() {
|
||||
@Override
|
||||
public void OIANotifyEvent(ECLOIAEvent event) {
|
||||
oiaNotifyEvents.incrementAndGet();
|
||||
}
|
||||
};
|
||||
|
||||
oia.RegisterOIAEvent(listener);
|
||||
oia.RegisterOIAEvent(notify);
|
||||
|
||||
// Mutate OIA state: lock keyboard
|
||||
inputProcessor.setKeyboardLocked(true);
|
||||
assertTrue(oiaListenerEvents.get() > 0);
|
||||
assertTrue(oiaNotifyEvents.get() > 0);
|
||||
|
||||
ECLOIAEvent event = lastOiaEvent.get();
|
||||
assertNotNull(event);
|
||||
assertSame(oia, event.getOIA());
|
||||
assertSame(oia, event.GetOIA());
|
||||
assertEquals(ECLOIAEvent.OIA_UPDATE, event.GetType());
|
||||
assertTrue(event.isInputInhibited());
|
||||
assertTrue(event.IsInputInhibited());
|
||||
|
||||
// Error notification
|
||||
ECLErr err = new ECLErr("ECLOIA", "OIA001", "OIA Comm Fail");
|
||||
oia.notifyOIAError(err);
|
||||
assertSame(err, oiaError.get());
|
||||
|
||||
// Unregister triggers stop
|
||||
oia.UnregisterOIAEvent(listener);
|
||||
assertEquals(ECLOIA.STOP_UNREGISTER, oiaStopReason.get());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5.4 ECLCommEvent and ECLCommListener Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testCommEventAndListenerSignatures() {
|
||||
ECLConnection conn = new ECLConnection();
|
||||
|
||||
AtomicInteger commListenerEvents = new AtomicInteger(0);
|
||||
AtomicBoolean commNotifyFlag = new AtomicBoolean(false);
|
||||
AtomicReference<ECLCommEvent> lastCommEvent = new AtomicReference<>();
|
||||
AtomicReference<ECLErr> commError = new AtomicReference<>();
|
||||
AtomicInteger commStopReason = new AtomicInteger(0);
|
||||
|
||||
ECLCommListener listener = new ECLCommListener() {
|
||||
@Override
|
||||
public void CommNotifyEvent(ECLCommEvent event) {
|
||||
commListenerEvents.incrementAndGet();
|
||||
lastCommEvent.set(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void CommNotifyError(ECLConnection c, ECLErr err) {
|
||||
commError.set(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void CommNotifyStop(ECLConnection c, int reason) {
|
||||
commStopReason.set(reason);
|
||||
}
|
||||
};
|
||||
|
||||
ECLCommNotify notify = new ECLCommNotify() {
|
||||
@Override
|
||||
public void CommNotify(boolean connected) {
|
||||
commNotifyFlag.set(connected);
|
||||
}
|
||||
};
|
||||
|
||||
conn.RegisterCommEvent(listener);
|
||||
conn.RegisterCommEvent(notify, false);
|
||||
|
||||
// Trigger connection state change
|
||||
ECLCommEvent testCommEvent = new ECLCommEvent(conn, ECLCommEvent.COMM_CONNECTED,
|
||||
ConnectionState.NOT_CONNECTED, ConnectionState.CONNECTED_3270, "Connected OK", "IBM-3278-2", "TSO01");
|
||||
|
||||
// Use reflection or error dispatch to trigger comm event
|
||||
conn.notifyCommError(new ECLErr("ECLConnection", "ERR01", "Comm failure"));
|
||||
assertNotNull(commError.get());
|
||||
|
||||
// Unregister triggers stop
|
||||
conn.UnregisterCommEvent(listener);
|
||||
assertEquals(ECLConnection.STOP_UNREGISTER, commStopReason.get());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5.5 ECLPSGraphicsEvent and ECLPSGraphicsListener Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testPSGraphicsEventAndListener() {
|
||||
ECLPSGraphicsServices gs = ps.GetPSGraphicsServices();
|
||||
assertNotNull(gs);
|
||||
|
||||
AtomicInteger graphicsEventCount = new AtomicInteger(0);
|
||||
AtomicInteger graphicsUpdatedCount = new AtomicInteger(0);
|
||||
AtomicReference<ECLPSGraphicsEvent> lastGEvent = new AtomicReference<>();
|
||||
|
||||
ECLPSGraphicsListener gListener = new ECLPSGraphicsListener() {
|
||||
@Override
|
||||
public void graphicsEvent(ECLPSGraphicsEvent event) {
|
||||
graphicsEventCount.incrementAndGet();
|
||||
lastGEvent.set(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void graphicsUpdated(ECLPSGraphicsEvent event) {
|
||||
graphicsUpdatedCount.incrementAndGet();
|
||||
lastGEvent.set(event);
|
||||
}
|
||||
};
|
||||
|
||||
gs.addGraphicsListener(gListener);
|
||||
|
||||
// Fire graphics update via mousePressed
|
||||
gs.mousePressed(50, 100, 1);
|
||||
assertEquals(1, graphicsUpdatedCount.get());
|
||||
assertNotNull(lastGEvent.get());
|
||||
assertEquals(ECLPSGraphicsEvent.GRAPHICS_UPDATED, lastGEvent.get().GetID());
|
||||
assertSame(ps, lastGEvent.get().GetPS());
|
||||
assertSame(ps, lastGEvent.get().getSource());
|
||||
|
||||
// Fire custom graphics event
|
||||
if (gs instanceof DefaultPSGraphicsServices) {
|
||||
((DefaultPSGraphicsServices) gs).fireGraphicsEvent(ECLPSGraphicsEvent.GRAPHICS_ACTIVATED);
|
||||
assertEquals(1, graphicsEventCount.get());
|
||||
assertEquals(ECLPSGraphicsEvent.GRAPHICS_ACTIVATED, lastGEvent.get().GetID());
|
||||
}
|
||||
|
||||
// Test with image and rectangle
|
||||
Image testImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
|
||||
Rectangle rect = new Rectangle(0, 0, 32, 32);
|
||||
ECLPSGraphicsEvent fullGEvent = new ECLPSGraphicsEvent(ps, ECLPSGraphicsEvent.GRAPHICS_UPDATED, testImage, rect);
|
||||
assertSame(testImage, fullGEvent.GetImage());
|
||||
assertSame(testImage, fullGEvent.getImage());
|
||||
assertEquals(rect, fullGEvent.GetRectangle());
|
||||
assertEquals(rect, fullGEvent.getRectangle());
|
||||
|
||||
// Remove listener
|
||||
gs.removeGraphicsListener(gListener);
|
||||
gs.mousePressed(50, 100, 1);
|
||||
assertEquals(1, graphicsUpdatedCount.get()); // Did not increase
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5.6 Drop-in Canonical eNetwork.ECL.event.* Facade Tests
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testDropInENetworkEventFacades() {
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.ECLPSUpdate eUpdate =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.ECLPSUpdate(ps, 0, 0, 23, 79, 0, 1919, true, "TEST");
|
||||
assertEquals(1, eUpdate.GetStartRow());
|
||||
assertEquals(24, eUpdate.GetEndRow());
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.event.ECLPSEvent ePsEvent =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.event.ECLPSEvent(ps, ECLPSEvent.PS_UPDATE, 0, 0, 5, 5, 0, 0, 24, 80, false);
|
||||
assertEquals(1, ePsEvent.GetStartRow());
|
||||
assertEquals(6, ePsEvent.GetEndRow());
|
||||
|
||||
AtomicBoolean listenerNotified = new AtomicBoolean(false);
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.event.ECLPSListener eListener =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.event.ECLPSListener() {
|
||||
@Override
|
||||
public void PSNotifyEvent(ECLPSEvent event) {
|
||||
listenerNotified.set(true);
|
||||
}
|
||||
};
|
||||
|
||||
ps.RegisterPSEvent(eListener);
|
||||
ps.notifyPSUpdate(0, 0, 1, 1, false);
|
||||
assertTrue(listenerNotified.get());
|
||||
ps.UnregisterPSEvent(eListener);
|
||||
|
||||
// OIA facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.event.ECLOIAEvent eOiaEvent =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.event.ECLOIAEvent(null, 1, 0, 0, false, "READY");
|
||||
assertEquals("READY", eOiaEvent.GetStatusString());
|
||||
|
||||
// Comm facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.event.ECLCommEvent eCommEvent =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.event.ECLCommEvent(null, 1, ConnectionState.NOT_CONNECTED,
|
||||
ConnectionState.CONNECTED_3270, "OK", "3278", "LU1");
|
||||
assertTrue(eCommEvent.IsConnected());
|
||||
|
||||
// Graphics facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.event.ECLPSGraphicsEvent eGraphicsEvent =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.event.ECLPSGraphicsEvent(ps, 5);
|
||||
assertEquals(5, eGraphicsEvent.GetID());
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class FillAreaTest {
|
||||
int red = 0xFFFF0000;
|
||||
int blue = 0xFF0000FF; // Background overpaint color
|
||||
area.fill(plane, red, 0, 5, false, 0,
|
||||
GocaConstants.LT_SOLID, GocaConstants.LW_NORMAL, GocaConstants.MIX_OVER, blue, null);
|
||||
GocaConstants.LT_SOLID, GocaConstants.LW_NORMAL, GocaConstants.BMX_OPAQUE, blue, null);
|
||||
|
||||
int[] buffer = plane.getRgbBuffer();
|
||||
int redCount = 0;
|
||||
@@ -94,6 +94,6 @@ public class FillAreaTest {
|
||||
}
|
||||
}
|
||||
assertTrue(redCount > 0, "Expected pattern foreground red pixels");
|
||||
assertTrue(blueCount > 0, "Expected pattern background blue pixels under MIX_OVER");
|
||||
assertTrue(blueCount > 0, "Expected pattern background blue pixels under BMX_OPAQUE");
|
||||
}
|
||||
}
|
||||
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
package haus.nightmare.lib3270j.graphics;
|
||||
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Verification test suite for Phase 1: Screen & Presentation Engine Fidelity.
|
||||
* Covers items 1.1 through 1.4 of the PhasedUpdates specification.
|
||||
*/
|
||||
public class Phase1ColorCalibrationTest {
|
||||
|
||||
// ====================================================================================================
|
||||
// ITEM 1.1: GOCA 16-Color Graphics Palette Calibration
|
||||
// ====================================================================================================
|
||||
@Test
|
||||
@DisplayName("Item 1.1: GOCA 16-color palette calibration matches IBM Host On-Demand")
|
||||
public void testItem1_1_GocaColorPaletteCalibration() {
|
||||
assertEquals(0xFFFFA200, GocaConstants.GOCA_COLORS[10], "GOCA Index 10 (Orange) must be 0xFFFFA200");
|
||||
assertEquals(0xFFA0A000, GocaConstants.GOCA_COLORS[14], "GOCA Index 14 (Mustard) must be 0xFFA0A000");
|
||||
|
||||
// Verify primary palette colors
|
||||
assertEquals(0xFF00FF00, GocaConstants.GOCA_COLORS[0], "Index 0: Default (Green)");
|
||||
assertEquals(0xFF7890F0, GocaConstants.GOCA_COLORS[1], "Index 1: Blue (0x7890F0)");
|
||||
assertEquals(0xFFFF0000, GocaConstants.GOCA_COLORS[2], "Index 2: Red");
|
||||
assertEquals(0xFFFF00FF, GocaConstants.GOCA_COLORS[3], "Index 3: Pink");
|
||||
assertEquals(0xFF00FF00, GocaConstants.GOCA_COLORS[4], "Index 4: Green");
|
||||
assertEquals(0xFF00FFFF, GocaConstants.GOCA_COLORS[5], "Index 5: Turquoise");
|
||||
assertEquals(0xFFFFFF00, GocaConstants.GOCA_COLORS[6], "Index 6: Yellow");
|
||||
assertEquals(0xFFFFFFFF, GocaConstants.GOCA_COLORS[7], "Index 7: Neutral White");
|
||||
assertEquals(0xFF000000, GocaConstants.GOCA_COLORS[8], "Index 8: Black");
|
||||
}
|
||||
|
||||
// ====================================================================================================
|
||||
// ITEM 1.2: Base 4-Color Model Realignment & ExtendedAttribute Helper
|
||||
// ====================================================================================================
|
||||
@Test
|
||||
@DisplayName("Item 1.2: Base 4-color model helper maps field attributes correctly")
|
||||
public void testItem1_2_Base4ColorMapping() {
|
||||
// Normal Unprotected: Green (0xF4)
|
||||
byte faNormalUnprotected = 0x00; // not protected, not high
|
||||
assertEquals(ExtendedAttribute.COLOR_BASE_NORMAL_UNPROTECT, ExtendedAttribute.getBase3270Color(faNormalUnprotected));
|
||||
assertEquals((byte) 0xF4, ExtendedAttribute.getBase3270Color(faNormalUnprotected));
|
||||
|
||||
// Intensified Unprotected: Red (0xF2)
|
||||
byte faIntensifiedUnprotected = 0x08; // high, not protected
|
||||
assertEquals(ExtendedAttribute.COLOR_BASE_INTENSIFY_UNPROTECT, ExtendedAttribute.getBase3270Color(faIntensifiedUnprotected));
|
||||
assertEquals((byte) 0xF2, ExtendedAttribute.getBase3270Color(faIntensifiedUnprotected));
|
||||
|
||||
// Normal Protected: Turquoise / Cyan (0xF5)
|
||||
byte faNormalProtected = 0x20; // protected, not high
|
||||
assertEquals(ExtendedAttribute.COLOR_BASE_NORMAL_PROTECT, ExtendedAttribute.getBase3270Color(faNormalProtected));
|
||||
assertEquals((byte) 0xF5, ExtendedAttribute.getBase3270Color(faNormalProtected));
|
||||
|
||||
// Intensified Protected: White (0xF7)
|
||||
byte faIntensifiedProtected = 0x28; // protected and high
|
||||
assertEquals(ExtendedAttribute.COLOR_BASE_INTENSIFY_PROTECT, ExtendedAttribute.getBase3270Color(faIntensifiedProtected));
|
||||
assertEquals((byte) 0xF7, ExtendedAttribute.getBase3270Color(faIntensifiedProtected));
|
||||
}
|
||||
|
||||
// ====================================================================================================
|
||||
// ITEM 1.3: HODTransparentColorFilter and HODColorChangeFilter
|
||||
// ====================================================================================================
|
||||
@Test
|
||||
@DisplayName("Item 1.3: HODTransparentColorFilter keys out background color to alpha 0x00")
|
||||
public void testItem1_3_TransparentColorFilter() {
|
||||
HODTransparentColorFilter filter = new HODTransparentColorFilter(Color.BLACK);
|
||||
assertEquals(0, filter.getTransparentRgb());
|
||||
|
||||
// Black pixel (0xFF000000) should become transparent (0x00000000)
|
||||
int filteredBlack = filter.filterRGB(0, 0, 0xFF000000);
|
||||
assertEquals(0x00000000, filteredBlack);
|
||||
|
||||
// Non-black pixel should be preserved
|
||||
int redPixel = 0xFFFF0000;
|
||||
int filteredRed = filter.filterRGB(0, 0, redPixel);
|
||||
assertEquals(redPixel, filteredRed);
|
||||
|
||||
// Change transparent color to white
|
||||
filter.setTransparentRgb(0xFFFFFF);
|
||||
int whitePixel = 0xFFFFFFFF;
|
||||
assertEquals(0x00000000, filter.filterRGB(0, 0, whitePixel));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Item 1.3: HODColorChangeFilter replaces old color with new color")
|
||||
public void testItem1_3_ColorChangeFilter() {
|
||||
HODColorChangeFilter filter = new HODColorChangeFilter(0x0000FF, 0xFFA200);
|
||||
|
||||
// Blue pixel should become Orange
|
||||
int bluePixel = 0xFF0000FF;
|
||||
int result = filter.filterRGB(0, 0, bluePixel);
|
||||
assertEquals(0xFFA200, result);
|
||||
|
||||
// Green pixel should remain unchanged
|
||||
int greenPixel = 0xFF00FF00;
|
||||
assertEquals(greenPixel, filter.filterRGB(0, 0, greenPixel));
|
||||
}
|
||||
|
||||
// ====================================================================================================
|
||||
// ITEM 1.4: ScreenBuffer Effective Attributes & Multi-plane Extraction
|
||||
// ====================================================================================================
|
||||
@Test
|
||||
@DisplayName("Item 1.4: ScreenBuffer effective attribute derivation and plane copying")
|
||||
public void testItem1_4_ScreenBufferEffectiveAttributes() {
|
||||
ScreenBuffer buffer = new ScreenBuffer(TerminalModel.IBM_3278_2, new EbcdicTranslator());
|
||||
|
||||
// Write a field header at position 0: normal unprotected
|
||||
buffer.setFieldAttribute(0, (byte) 0x00);
|
||||
|
||||
// Character at row 0, col 1: no explicit EA attributes -> should inherit base color (0xF4 Green)
|
||||
ExtendedAttribute ea1 = new ExtendedAttribute();
|
||||
ea1.ucs4 = 'A';
|
||||
buffer.setExtAttr(0, 1, ea1);
|
||||
|
||||
assertEquals((byte) 0xF4, buffer.getEffectiveForegroundColor(1));
|
||||
assertEquals((byte) 0x00, buffer.getEffectiveBackgroundColor(1));
|
||||
assertEquals((byte) 0x00, buffer.getEffectiveHighlighting(1));
|
||||
|
||||
// Character at row 0, col 2: explicit EA foreground (0xF1 Blue) and GR (underline)
|
||||
ExtendedAttribute ea2 = new ExtendedAttribute();
|
||||
ea2.ucs4 = 'B';
|
||||
ea2.fg = (byte) 0xF1;
|
||||
ea2.gr = ExtendedAttribute.GR_UNDERLINE;
|
||||
buffer.setExtAttr(0, 2, ea2);
|
||||
|
||||
assertEquals((byte) 0xF1, buffer.getEffectiveForegroundColor(2));
|
||||
assertEquals(ExtendedAttribute.GR_UNDERLINE, buffer.getEffectiveHighlighting(2));
|
||||
|
||||
// Test plane copying
|
||||
char[] textPlane = new char[80];
|
||||
buffer.copyPlanes(ScreenBuffer.PLANE_TEXT, textPlane, 0, 80);
|
||||
assertEquals('A', textPlane[1]);
|
||||
assertEquals('B', textPlane[2]);
|
||||
|
||||
char[] fgPlane = new char[80];
|
||||
buffer.copyPlanes(ScreenBuffer.PLANE_COLOR, fgPlane, 0, 80);
|
||||
assertEquals((char) 0xF4, fgPlane[1]);
|
||||
assertEquals((char) 0xF1, fgPlane[2]);
|
||||
|
||||
char[] grPlane = new char[80];
|
||||
buffer.copyPlanes(ScreenBuffer.PLANE_HILITE, grPlane, 0, 80);
|
||||
assertEquals((char) 0x00, grPlane[1]);
|
||||
assertEquals((char) ExtendedAttribute.GR_UNDERLINE, grPlane[2]);
|
||||
}
|
||||
|
||||
// ====================================================================================================
|
||||
// ITEM 1.4 (GOCA Fill Area): Background Mix Modes (BMX_OPAQUE vs BMX_TRANSPARENT)
|
||||
// ====================================================================================================
|
||||
@Test
|
||||
@DisplayName("Item 1.4: FillArea respects BMX_OPAQUE and BMX_TRANSPARENT background mixing")
|
||||
public void testItem1_4_FillAreaBackgroundMix() {
|
||||
GraphicsPlane plane = new GraphicsPlane(10, 10);
|
||||
|
||||
// Test transparent black fill: should not paint when BMX_TRANSPARENT
|
||||
FillArea fillAreaTrans = new FillArea(GocaConstants.FILL_RULE_EVEN_ODD);
|
||||
fillAreaTrans.addEdge(0, 0, 10, 0);
|
||||
fillAreaTrans.addEdge(10, 0, 10, 10);
|
||||
fillAreaTrans.addEdge(10, 10, 0, 10);
|
||||
fillAreaTrans.addEdge(0, 10, 0, 0);
|
||||
fillAreaTrans.fill(plane, 0xFF000000, 0, GocaConstants.PT_SOLID, false, 0, 0, 1,
|
||||
GocaConstants.BMX_TRANSPARENT, 0xFF0000FF, GocaConstants.FILL_RULE_EVEN_ODD, null);
|
||||
|
||||
// Verify plane pixels remain unpainted (transparent / 0)
|
||||
assertEquals(0, plane.getRgbBuffer()[0]);
|
||||
|
||||
// Test opaque background fill: with patterned fill and BMX_OPAQUE
|
||||
GraphicsPlane plane2 = new GraphicsPlane(8, 8);
|
||||
FillArea fillAreaOpaque = new FillArea(GocaConstants.FILL_RULE_EVEN_ODD);
|
||||
fillAreaOpaque.addEdge(0, 0, 8, 0);
|
||||
fillAreaOpaque.addEdge(8, 0, 8, 8);
|
||||
fillAreaOpaque.addEdge(8, 8, 0, 8);
|
||||
fillAreaOpaque.addEdge(0, 8, 0, 0);
|
||||
fillAreaOpaque.fill(plane2, 0xFFFF0000, 0, 1, false, 0, 0, 1,
|
||||
GocaConstants.BMX_OPAQUE, 0xFF0000FF, GocaConstants.FILL_RULE_EVEN_ODD, null);
|
||||
|
||||
// At least one pixel should be Red (pattern foreground) and at least one Blue (opaque background)
|
||||
boolean hasRed = false;
|
||||
boolean hasBlue = false;
|
||||
for (int p : plane2.getRgbBuffer()) {
|
||||
if (p == 0xFFFF0000) hasRed = true;
|
||||
if (p == 0xFF0000FF) hasBlue = true;
|
||||
}
|
||||
assertTrue(hasRed, "FillArea should render foreground pattern pixels");
|
||||
assertTrue(hasBlue, "FillArea should render opaque background pixels when BMX_OPAQUE");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
package haus.nightmare.lib3270j.graphics;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Comprehensive test suite verifying Phase 2: Host Vector Graphics & GOCA Rendering Engine.
|
||||
*/
|
||||
public class Phase2GocaEngineTest {
|
||||
|
||||
private GraphicsPlane plane;
|
||||
private GocaDecoder decoder;
|
||||
private GddmCoordinateTransform transform;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
plane = new GraphicsPlane(720, 384);
|
||||
decoder = new GocaDecoder(plane);
|
||||
transform = new GddmCoordinateTransform(80, 24, 9, 16);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test GOCA Image Orders: 0xD1, 0x92, 0x93 (Standard End Image)")
|
||||
public void testStandardImageOrders() {
|
||||
// 0xD1: Begin Image Absolute (x=10, y=20, w=8, h=8, bpp=1, comp=0)
|
||||
// 0x92: Image Data (8 bytes, each with 0xAA)
|
||||
// 0x93: End Image
|
||||
byte[] stream = new byte[] {
|
||||
(byte) 0xD1, 0x0A,
|
||||
0x00, 0x0A, // x = 10
|
||||
0x00, 0x14, // y = 20
|
||||
0x00, 0x08, // w = 8
|
||||
0x00, 0x08, // h = 8
|
||||
0x01, // 1 bpp
|
||||
0x00, // uncompressed
|
||||
(byte) 0x92, 0x08,
|
||||
(byte) 0xAA, (byte) 0x55, (byte) 0xAA, (byte) 0x55,
|
||||
(byte) 0xAA, (byte) 0x55, (byte) 0xAA, (byte) 0x55,
|
||||
(byte) 0x93, 0x00
|
||||
};
|
||||
|
||||
decoder.decodeGoca(stream, 0, stream.length);
|
||||
assertTrue(plane.hasContent(), "Graphics plane should have raster content after image order");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test GOCA Image Orders: 0x91 (Begin Image Current Position) and 0x93 (End Image)")
|
||||
public void testBeginImageCurrentPosition() {
|
||||
// Set current position to (30, 40) via G_GCLINE or G_GSCOL
|
||||
decoder.setGraphicCursorPosition(30, 40);
|
||||
|
||||
// 0x91: Begin Image Current Position (w=8, h=8, bpp=1, comp=0)
|
||||
// 0x92: Image Data (8 bytes)
|
||||
// 0x93: End Image
|
||||
byte[] stream = new byte[] {
|
||||
(byte) 0x91, 0x06,
|
||||
0x00, 0x08, // w = 8
|
||||
0x00, 0x08, // h = 8
|
||||
0x01, // 1 bpp
|
||||
0x00, // uncompressed
|
||||
(byte) 0x92, 0x08,
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0x93, 0x00
|
||||
};
|
||||
|
||||
decoder.decodeGoca(stream, 0, stream.length);
|
||||
assertTrue(plane.hasContent(), "Graphics plane should have raster content from current position image");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Procedure Orders: P_ATTCUR, P_SETCUR, P_DETCUR")
|
||||
public void testCursorProcedureOrders() {
|
||||
// P_ATTCUR (0x08), P_SETCUR (0x31: len=4, x=150, y=250), P_DETCUR (0x09)
|
||||
byte[] proc = new byte[] {
|
||||
0x08, 0x00, // Attach cursor
|
||||
0x31, 0x04, 0x00, (byte) 150, 0x00, (byte) 250, // Set cursor (150, 250)
|
||||
};
|
||||
|
||||
decoder.processProcedureOrders(proc, 0, proc.length);
|
||||
assertTrue(decoder.isGraphicsCursorActive(), "Graphic cursor should be active after P_ATTCUR");
|
||||
assertTrue(plane.isGraphicCursorAttached(), "Plane graphic cursor should be attached");
|
||||
assertEquals(150, decoder.getGraphicCursorX());
|
||||
assertEquals(250, decoder.getGraphicCursorY());
|
||||
assertEquals(150, plane.getGraphicCursorX());
|
||||
assertEquals(250, plane.getGraphicCursorY());
|
||||
assertEquals(GocaConstants.GCURSOR_SHAPE_CROSSHAIR, plane.getHodCursorShape());
|
||||
|
||||
// Detach cursor
|
||||
byte[] detach = new byte[] { 0x09, 0x00 };
|
||||
decoder.processProcedureOrders(detach, 0, detach.length);
|
||||
assertFalse(decoder.isGraphicsCursorActive(), "Graphic cursor should be inactive after P_DETCUR");
|
||||
assertFalse(plane.isGraphicCursorAttached(), "Plane graphic cursor should be detached");
|
||||
assertEquals(GocaConstants.GCURSOR_SHAPE_BOX, plane.getHodCursorShape());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test GddmCoordinateTransform 3179G Conversion Methods")
|
||||
public void testCoordinateTransformConversions() {
|
||||
// 80 cols x 24 rows, cell pitch 9x16 -> 720x384
|
||||
assertEquals(1, transform.convertAddressToRow(0));
|
||||
assertEquals(1, transform.convertAddressToColumn(0));
|
||||
assertEquals(2, transform.convertAddressToRow(80));
|
||||
assertEquals(1, transform.convertAddressToColumn(80));
|
||||
assertEquals(2, transform.convertAddressToRow(85));
|
||||
assertEquals(6, transform.convertAddressToColumn(85));
|
||||
|
||||
assertEquals(0, transform.convertColumnToX(0));
|
||||
assertEquals(18, transform.convertColumnToX(2));
|
||||
assertEquals(0, transform.convertRowToY(0));
|
||||
assertEquals(32, transform.convertRowToY(2));
|
||||
|
||||
assertEquals(1, transform.convertXtoColumn(0));
|
||||
assertEquals(2, transform.convertXtoColumn(9));
|
||||
assertEquals(1, transform.convertYtoRow(0));
|
||||
assertEquals(2, transform.convertYtoRow(16));
|
||||
assertEquals(2, transform.convertXYToRow(100, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Triple-Plane Programmed Symbols Composite Colors against Calibrated GOCA Palette")
|
||||
public void testTriplePlaneCompositeColors() {
|
||||
ProgramSymbolSet pss = new ProgramSymbolSet(true); // Triple plane
|
||||
pss.setLcid(0x40);
|
||||
|
||||
// Define a 2x2 symbol where pixel 0=1 (Red), pixel 1=2 (Green), pixel 2=4 (Blue), pixel 3=7 (White)
|
||||
byte[] raw = new byte[] { 1, 2, 4, 7 };
|
||||
ProgramSymbolSet.SymbolSlot slot = new ProgramSymbolSet.SymbolSlot(2, 2, raw, true);
|
||||
|
||||
int[] rgb = slot.getRgbPixels(0xFFFFFFFF, 0x00000000);
|
||||
assertEquals(GocaConstants.GOCA_COLORS[2], rgb[0], "Mask 1 should map to calibrated Red");
|
||||
assertEquals(GocaConstants.GOCA_COLORS[4], rgb[1], "Mask 2 should map to calibrated Green");
|
||||
assertEquals(GocaConstants.GOCA_COLORS[1], rgb[2], "Mask 4 should map to calibrated Blue (0xFF7890F0)");
|
||||
assertEquals(GocaConstants.GOCA_COLORS[7], rgb[3], "Mask 7 should map to calibrated White");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Edge Class Scanline Algorithm and Quicksort")
|
||||
public void testEdgeClass() {
|
||||
Edge e1 = new Edge(10, 20, 30, 40);
|
||||
assertEquals(20, e1.ymin);
|
||||
assertEquals(40, e1.ymax);
|
||||
assertTrue(e1.dy > 0);
|
||||
|
||||
int scanX = e1.edgeScan();
|
||||
assertTrue(scanX >= 10);
|
||||
|
||||
Edge[] edges = new Edge[] {
|
||||
new Edge(50, 10, 50, 20),
|
||||
new Edge(20, 10, 20, 20),
|
||||
new Edge(80, 10, 80, 20)
|
||||
};
|
||||
Edge.quicksort(edges, edges.length);
|
||||
assertTrue(edges[0].xi <= edges[1].xi);
|
||||
assertTrue(edges[1].xi <= edges[2].xi);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test HODBounds and HODTransform")
|
||||
public void testHODBoundsAndTransform() {
|
||||
HODBounds bounds = new HODBounds();
|
||||
bounds.set(10, 20, 100, 200);
|
||||
assertEquals(10, bounds.getHODUpperLeftX());
|
||||
assertEquals(20, bounds.getHODUpperLeftY());
|
||||
assertEquals(100, bounds.getHODLowerRightX());
|
||||
assertEquals(200, bounds.getHODLowerRightY());
|
||||
assertTrue(bounds.isHODValid());
|
||||
assertEquals(91, bounds.getWidth());
|
||||
assertEquals(181, bounds.getHeight());
|
||||
|
||||
Rectangle r = bounds.toHODRectangle();
|
||||
assertEquals(10, r.x);
|
||||
assertEquals(20, r.y);
|
||||
|
||||
HODTransform trans = new HODTransform(18, 32, 9, 16); // 2x scale
|
||||
assertEquals(2.0, trans.getXTrans(), 0.001);
|
||||
assertEquals(2.0, trans.getYTrans(), 0.001);
|
||||
|
||||
Point pt = trans.calculate(new Point(10, 20));
|
||||
assertEquals(20, pt.x);
|
||||
assertEquals(40, pt.y);
|
||||
|
||||
Rectangle scaledR = trans.calculate(new Rectangle(5, 10, 15, 25));
|
||||
assertEquals(10, scaledR.x);
|
||||
assertEquals(20, scaledR.y);
|
||||
assertEquals(30, scaledR.width);
|
||||
assertEquals(50, scaledR.height);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test HODWallpaper Tile, Center, and Stretch")
|
||||
public void testHODWallpaper() {
|
||||
BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
|
||||
HODWallpaper wp = new HODWallpaper(img, HODWallpaper.HOD_CENTER);
|
||||
assertEquals(HODWallpaper.HOD_CENTER, wp.getDisplay());
|
||||
|
||||
wp.setDisplay(HODWallpaper.HOD_TILE);
|
||||
assertEquals(HODWallpaper.HOD_TILE, wp.getDisplay());
|
||||
|
||||
wp.setDisplay(HODWallpaper.HOD_STRETCH);
|
||||
assertEquals(HODWallpaper.HOD_STRETCH, wp.getDisplay());
|
||||
|
||||
BufferedImage canvas = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics g = canvas.getGraphics();
|
||||
wp.paint(new java.awt.Canvas(), g, 0, 0, 100, 100);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test HODGraphicsPlane Headless Facade Delegating to GraphicsPlane")
|
||||
public void testHODGraphicsPlaneFacade() {
|
||||
HODGraphicsPlane hodPlane = new HODGraphicsPlane(720, 384);
|
||||
hodPlane.setHODGraphColor(2); // Red
|
||||
hodPlane.drawHODLine(10, 10, 50, 50);
|
||||
|
||||
HODBounds b = hodPlane.getHODBounds();
|
||||
assertTrue(b.getHODUpperLeftX() <= 10);
|
||||
assertTrue(b.getHODLowerRightX() >= 50);
|
||||
|
||||
hodPlane.drawHODArc(60, 60, 40, 40, 0, 180);
|
||||
hodPlane.fillHODArc(100, 100, 30, 30, 0, 360);
|
||||
|
||||
FillArea area = new FillArea(new int[] { 10, 30, 20 }, new int[] { 10, 10, 30 }, new int[] { 3 }, 1, Color.BLUE);
|
||||
hodPlane.fillHODArea(area);
|
||||
|
||||
assertNotNull(hodPlane.getHODImage());
|
||||
assertTrue(hodPlane.getDelegate().hasContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test HODProgramSymbolManager Facade")
|
||||
public void testHODProgramSymbolManagerFacade() {
|
||||
ProgramSymbolManager psm = new ProgramSymbolManager();
|
||||
HODProgramSymbolManager hodPsm = new HODProgramSymbolManager(psm);
|
||||
|
||||
// Load a 1-character single-plane symbol set
|
||||
byte[] loadps = new byte[22];
|
||||
loadps[0] = 0x01; // format 1
|
||||
loadps[1] = 0x41; // LCID 0x41
|
||||
loadps[2] = 0x41; // codepoint 0x41
|
||||
loadps[3] = 0x02; // RWS 2
|
||||
for (int i = 4; i < 22; i++) loadps[i] = (byte) 0xAA;
|
||||
|
||||
psm.loadps(loadps);
|
||||
assertNotNull(hodPsm.getSymbol(0x41, 0x41));
|
||||
|
||||
HODBitImage bi = hodPsm.getHODBitImage(0x41, 0x41);
|
||||
assertNotNull(bi);
|
||||
assertEquals(9, bi.getImageSize().width);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test FilletPts and FillArea Compatibility Signatures")
|
||||
public void testFilletAndFillAreaCompatibility() {
|
||||
FilletPts fillet = new FilletPts();
|
||||
assertEquals(60, fillet.getPointsRequired(3));
|
||||
|
||||
int[] xIn = new int[] { 0, 50, 100 };
|
||||
int[] yIn = new int[] { 0, 100, 0 };
|
||||
int[] xOut = new int[100];
|
||||
int[] yOut = new int[100];
|
||||
int count = fillet.getFilletPoints(xIn, yIn, 3, xOut, yOut);
|
||||
assertTrue(count > 0, "Fillet points should be generated");
|
||||
|
||||
FillArea fa = new FillArea(new int[] { 0, 50, 100 }, new int[] { 0, 100, 0 }, new int[] { 3 }, 1, Color.RED);
|
||||
fa.setFillModeOR();
|
||||
assertTrue(fa.isFillModeOR());
|
||||
fa.set8x8Pattern(new byte[] { -1, 0, -1, 0, -1, 0, -1, 0 });
|
||||
Rectangle bounds = fa.getBounds();
|
||||
assertEquals(0, bounds.x);
|
||||
assertEquals(0, bounds.y);
|
||||
assertEquals(101, bounds.width);
|
||||
assertEquals(101, bounds.height);
|
||||
|
||||
Image img = fa.getImage();
|
||||
assertNotNull(img);
|
||||
fa.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Drop-in HostGraphics Package Facades")
|
||||
public void testDropInPackageFacades() {
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODBounds b = new haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODBounds();
|
||||
b.set(5, 5, 25, 25);
|
||||
assertEquals(21, b.getWidth());
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.Edge e = new haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.Edge(0, 0, 10, 10);
|
||||
assertEquals(10, e.ymax);
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODTransform t = new haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODTransform(9, 16, 9, 16);
|
||||
assertEquals(1.0, t.getXTrans(), 0.001);
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODWallpaper w = new haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODWallpaper(0);
|
||||
assertEquals(0, w.getDisplay());
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODGraphicsPlane gp = new haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODGraphicsPlane();
|
||||
assertNotNull(gp.getDelegate());
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODColorChangeFilter cf = new haus.nightmare.lib3270j.eNetwork.ECL.hostgraphics.HODColorChangeFilter(0xFF00FF00);
|
||||
assertNotNull(cf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package haus.nightmare.lib3270j.printer;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.protocol.DS3270Constants;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for Phase 7 DS3270P 3270 printer data stream processor, short[] and byte[] ingestion,
|
||||
* LU1 (SCS) and LU3 dispatch, WSF LU1 structured fields, WCC processing, and lifecycle states.
|
||||
*/
|
||||
public class DS3270PPhase7Test {
|
||||
|
||||
private PrinterConfig config;
|
||||
private PD3270 pd;
|
||||
private EbcdicTranslator translator;
|
||||
private PrintSCS3270 scs;
|
||||
private PrintPS3270 printPs;
|
||||
private DS3270P ds3270p;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
config = new PrinterConfig("localhost", 23);
|
||||
config.setFormFeedAtEoj(true);
|
||||
config.setAutoFlushOnEoj(true);
|
||||
pd = new PD3270(config);
|
||||
pd.openPrinter(null);
|
||||
translator = new EbcdicTranslator("037");
|
||||
scs = new PrintSCS3270(config, pd, translator);
|
||||
printPs = new PrintPS3270(config, pd, translator);
|
||||
ds3270p = new DS3270P(null, config, pd, scs, printPs, translator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorsAndAccessors() {
|
||||
DS3270P defaultDs = new DS3270P();
|
||||
assertNotNull(defaultDs.getConfig());
|
||||
assertNotNull(defaultDs.getPD());
|
||||
assertNotNull(defaultDs.getPrintSCS());
|
||||
assertNotNull(defaultDs.getPrintPS());
|
||||
assertNotNull(defaultDs.getTranslator());
|
||||
|
||||
DS3270P configDs = new DS3270P(config);
|
||||
assertSame(config, configDs.getConfig());
|
||||
|
||||
DS3270P partialDs = new DS3270P(config, pd, translator);
|
||||
assertSame(pd, partialDs.getPD());
|
||||
assertSame(translator, partialDs.getTranslator());
|
||||
|
||||
ds3270p.setActiveLuType(PrinterConstants.LU_TYPE_1_SCS);
|
||||
assertEquals(PrinterConstants.LU_TYPE_1_SCS, ds3270p.getActiveLuType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDataLU1WithByteArrayAndShortArray() {
|
||||
ds3270p.setActiveLuType(PrinterConstants.LU_TYPE_1_SCS);
|
||||
|
||||
// Byte array test
|
||||
byte[] text1 = translator.stringToEbcdic("LINE 1 VIA BYTE ARRAY");
|
||||
ByteArrayOutputStream stream1 = new ByteArrayOutputStream();
|
||||
stream1.write(text1, 0, text1.length);
|
||||
stream1.write(PrinterConstants.SCS_NL);
|
||||
byte[] payload1 = stream1.toByteArray();
|
||||
|
||||
int consumed = ds3270p.receiveData(payload1, 0, payload1.length);
|
||||
assertEquals(payload1.length, consumed);
|
||||
|
||||
// Short array test
|
||||
byte[] text2 = translator.stringToEbcdic("LINE 2 VIA SHORT ARRAY");
|
||||
short[] sArray = new short[text2.length + 1];
|
||||
for (int i = 0; i < text2.length; i++) {
|
||||
sArray[i] = (short) (text2[i] & 0xFF);
|
||||
}
|
||||
sArray[text2.length] = (short) PrinterConstants.SCS_NL;
|
||||
|
||||
consumed = ds3270p.receiveData(sArray, 0, sArray.length);
|
||||
assertEquals(sArray.length, consumed);
|
||||
|
||||
ds3270p.endOfRecord();
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
assertTrue(captured.contains("LINE 1 VIA BYTE ARRAY"));
|
||||
assertTrue(captured.contains("LINE 2 VIA SHORT ARRAY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDataLU3With3270DS() {
|
||||
ds3270p.setActiveLuType(PrinterConstants.LU_TYPE_3_DS);
|
||||
|
||||
byte[] text = translator.stringToEbcdic("LU3 VIA DS3270P");
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
stream.write(DS3270Constants.CMD_WRITE);
|
||||
stream.write(PrinterConstants.WCC_START_PRINT_BIT); // Start print
|
||||
stream.write(PrinterConstants.ORDER_SBA);
|
||||
stream.write(0x40);
|
||||
stream.write(0x40);
|
||||
stream.write(text, 0, text.length);
|
||||
|
||||
byte[] payload = stream.toByteArray();
|
||||
int consumed = ds3270p.receiveData(payload, 0, payload.length);
|
||||
assertEquals(payload.length, consumed);
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
assertTrue(captured.contains("LU3 VIA DS3270P"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDataLU1PendingBuffer() {
|
||||
ds3270p.setActiveLuType(PrinterConstants.LU_TYPE_1_SCS);
|
||||
|
||||
byte[] part1 = translator.stringToEbcdic("PARTIAL ");
|
||||
byte[] part2 = translator.stringToEbcdic("LINE BUFFERED");
|
||||
ds3270p.bufferDataLU1(part1, 0, part1.length);
|
||||
ds3270p.bufferDataLU1(part2, 0, part2.length);
|
||||
|
||||
// Process buffered data
|
||||
ds3270p.receiveDataLU1();
|
||||
ds3270p.endOfRecord();
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
assertTrue(captured.contains("PARTIAL LINE BUFFERED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessWSFLU1StructuredField() {
|
||||
ds3270p.setActiveLuType(PrinterConstants.LU_TYPE_1_SCS);
|
||||
|
||||
byte[] msg = translator.stringToEbcdic("STRUCTURED FIELD DATA");
|
||||
ByteArrayOutputStream sfStream = new ByteArrayOutputStream();
|
||||
// SF length = 2 (len bytes) + 1 (id byte) + msg.length
|
||||
int sfLen = 3 + msg.length;
|
||||
sfStream.write((sfLen >> 8) & 0xFF);
|
||||
sfStream.write(sfLen & 0xFF);
|
||||
sfStream.write(0x65); // SCS data unit SF ID
|
||||
sfStream.write(msg, 0, msg.length);
|
||||
|
||||
byte[] sfPayload = sfStream.toByteArray();
|
||||
ds3270p.processWSFLU1(sfPayload, 0, sfPayload.length);
|
||||
ds3270p.endOfRecord();
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
assertTrue(captured.contains("STRUCTURED FIELD DATA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessWCCAndEndOfFile() {
|
||||
ds3270p.setActiveLuType(PrinterConstants.LU_TYPE_3_DS);
|
||||
|
||||
// Test WCC with Start Print = 0 schedules flush
|
||||
ds3270p.processWCC((short) 0x00);
|
||||
|
||||
// Test WCC with Start Print = 1 flushes immediately
|
||||
ds3270p.processWCC((short) PrinterConstants.WCC_START_PRINT_BIT);
|
||||
|
||||
// End of file
|
||||
ds3270p.endOfFile();
|
||||
assertEquals(1, pd.getPageCount());
|
||||
|
||||
// Reset state
|
||||
ds3270p.reset();
|
||||
assertEquals(0, pd.getPageCount());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package haus.nightmare.lib3270j.printer;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for Phase 7 DBCS printer extensions: PrintSCS3270DB and PrintPS3270DB.
|
||||
* Verifies SO/SI state switching, double-byte character decoding, 2-cell spacing,
|
||||
* ideographic spaces, and grid/ruling line attributes.
|
||||
*/
|
||||
public class PrintDBCSPhase7Test {
|
||||
|
||||
private PrinterConfig config;
|
||||
private PD3270 pd;
|
||||
private EbcdicTranslator translator;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
// Use Japanese DBCS code page Cp930
|
||||
config = new PrinterConfig("localhost", 23);
|
||||
config.setCodePage("930");
|
||||
pd = new PD3270(config);
|
||||
pd.openPrinter(null);
|
||||
translator = new EbcdicTranslator("930");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintSCS3270DBSOSIModeSwitchingAndCharacterSpacing() {
|
||||
PrintSCS3270DB scsDb = new PrintSCS3270DB(config, pd, translator);
|
||||
assertFalse(scsDb.isDBCSMode());
|
||||
assertEquals(5, scsDb.getDBCSCPI());
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
// SBCS 'A' (0xC1)
|
||||
stream.write(0xC1);
|
||||
|
||||
// Shift Out (0x0E) to enter DBCS
|
||||
stream.write(PrinterConstants.SCS_SO);
|
||||
|
||||
// Ideographic blank (0x40 0x40 in EBCDIC)
|
||||
stream.write(0x40);
|
||||
stream.write(0x40);
|
||||
|
||||
// Shift In (0x0F) to return to SBCS
|
||||
stream.write(PrinterConstants.SCS_SI);
|
||||
|
||||
// SBCS 'B' (0xC2)
|
||||
stream.write(0xC2);
|
||||
|
||||
byte[] payload = stream.toByteArray();
|
||||
scsDb.processHostData(payload, 0, payload.length);
|
||||
scsDb.flushLineBuffer();
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
assertNotNull(captured);
|
||||
assertTrue(captured.contains("A"));
|
||||
assertTrue(captured.contains("\u3000"));
|
||||
assertTrue(captured.contains("B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintSCS3270DBGridLines() {
|
||||
PrintSCS3270DB scsDb = new PrintSCS3270DB(config, pd, translator);
|
||||
|
||||
// Configure all 4 border grid lines
|
||||
scsDb.setGridLines(true, true, true, true);
|
||||
assertTrue(scsDb.hasLeftGrid());
|
||||
assertTrue(scsDb.hasRightGrid());
|
||||
assertTrue(scsDb.hasTopGrid());
|
||||
assertTrue(scsDb.hasBottomGrid());
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
stream.write(PrinterConstants.SCS_SO);
|
||||
stream.write(0x40);
|
||||
stream.write(0x40);
|
||||
stream.write(PrinterConstants.SCS_SI);
|
||||
|
||||
scsDb.processHostData(stream.toByteArray(), 0, stream.size());
|
||||
scsDb.flushLineBuffer();
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
// Box borders should be present in captured output
|
||||
assertTrue(captured.contains("┌"));
|
||||
assertTrue(captured.contains("┐"));
|
||||
assertTrue(captured.contains("└"));
|
||||
assertTrue(captured.contains("┘"));
|
||||
assertTrue(captured.contains("│"));
|
||||
|
||||
// Clear grid lines
|
||||
scsDb.clearGridLines();
|
||||
assertEquals(0, scsDb.getGridFlags());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintPS3270DBDBCSBufferingAndGridLines() {
|
||||
PrintPS3270DB psDb = new PrintPS3270DB(config, pd, translator);
|
||||
|
||||
// Place a DBCS character '日' at row 0, col 5
|
||||
char kanji = '\u65E5';
|
||||
psDb.setDBCSChar(0, 5, kanji);
|
||||
|
||||
int addrLeft = 5;
|
||||
int addrRight = 6;
|
||||
assertTrue(psDb.isDBCSCell(addrLeft));
|
||||
assertTrue(psDb.isDBCSLeft(addrLeft));
|
||||
assertTrue(psDb.isDBCSCell(addrRight));
|
||||
assertTrue(psDb.isDBCSRight(addrRight));
|
||||
assertFalse(psDb.isDBCSCell(0));
|
||||
|
||||
// Set top and bottom grid lines on the row
|
||||
psDb.setGridLine(addrLeft, PrintPS3270DB.GRID_TOP | PrintPS3270DB.GRID_LEFT);
|
||||
psDb.setGridLine(addrRight, PrintPS3270DB.GRID_BOTTOM | PrintPS3270DB.GRID_RIGHT);
|
||||
|
||||
assertEquals(PrintPS3270DB.GRID_TOP | PrintPS3270DB.GRID_LEFT, psDb.getGridLine(addrLeft));
|
||||
assertEquals(PrintPS3270DB.GRID_BOTTOM | PrintPS3270DB.GRID_RIGHT, psDb.getGridLine(addrRight));
|
||||
|
||||
// Format and render line 0
|
||||
psDb.printLine(0, 80);
|
||||
|
||||
String captured = pd.getCapturedText();
|
||||
assertTrue(captured.contains(String.valueOf(kanji)));
|
||||
assertTrue(captured.contains("┌") || captured.contains("│") || captured.contains("─"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintPS3270DBEraseBuffer() {
|
||||
PrintPS3270DB psDb = new PrintPS3270DB(config, pd, translator);
|
||||
psDb.setDBCSChar(1, 10, '\u672C');
|
||||
psDb.setGridLine(10, PrintPS3270DB.GRID_TOP);
|
||||
|
||||
assertTrue(psDb.isDBCSCell(1 * 80 + 10));
|
||||
psDb.erasePrintBuffer();
|
||||
assertFalse(psDb.isDBCSCell(1 * 80 + 10));
|
||||
assertEquals(0, psDb.getGridLine(10));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package haus.nightmare.lib3270j.printer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests verifying instantiation, inheritance, and compatibility of classes in:
|
||||
* - haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.*
|
||||
* - haus.nightmare.lib3270j.tn3270p.*
|
||||
*/
|
||||
public class TN3270PPackagePhase7Test {
|
||||
|
||||
@Test
|
||||
public void testENetworkECLTN3270PPackageClasses() {
|
||||
PrinterConfig config = new PrinterConfig("localhost", 23);
|
||||
|
||||
// Telnet3270EP
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.Telnet3270EP ep =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.Telnet3270EP(config);
|
||||
assertNotNull(ep);
|
||||
assertTrue(ep instanceof haus.nightmare.lib3270j.printer.Telnet3270EP);
|
||||
|
||||
// DS3270P
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.DS3270P ds =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.DS3270P(config);
|
||||
assertNotNull(ds);
|
||||
assertTrue(ds instanceof haus.nightmare.lib3270j.printer.DS3270P);
|
||||
|
||||
// PD3270
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PD3270 pd =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PD3270(config);
|
||||
assertNotNull(pd);
|
||||
assertTrue(pd instanceof haus.nightmare.lib3270j.printer.PD3270);
|
||||
|
||||
// PrintSCS3270
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintSCS3270 scs =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintSCS3270(config, pd, null);
|
||||
assertNotNull(scs);
|
||||
assertTrue(scs instanceof haus.nightmare.lib3270j.printer.PrintSCS3270);
|
||||
|
||||
// PrintPS3270
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintPS3270 printPs =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintPS3270(config, pd, null);
|
||||
assertNotNull(printPs);
|
||||
assertTrue(printPs instanceof haus.nightmare.lib3270j.printer.PrintPS3270);
|
||||
|
||||
// PrintSCS3270DB
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintSCS3270DB scsDb =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintSCS3270DB(config);
|
||||
assertNotNull(scsDb);
|
||||
assertTrue(scsDb instanceof haus.nightmare.lib3270j.printer.PrintSCS3270DB);
|
||||
|
||||
// PrintPS3270DB
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintPS3270DB psDb =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrintPS3270DB(config);
|
||||
assertNotNull(psDb);
|
||||
assertTrue(psDb instanceof haus.nightmare.lib3270j.printer.PrintPS3270DB);
|
||||
|
||||
// PrinterDefinitionTable & PDT
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrinterDefinitionTable pdt =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PrinterDefinitionTable("TEST", "Desc");
|
||||
assertEquals("TEST", pdt.getName());
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PDT pdtAlias =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.PDT("ALIAS", "Desc");
|
||||
assertEquals("ALIAS", pdtAlias.getName());
|
||||
|
||||
// Timer & TimerEvent
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.Timer timer =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.Timer(100);
|
||||
assertNotNull(timer);
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.TimerEvent event =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270p.TimerEvent(timer, "Timer1");
|
||||
assertEquals("Timer1", event.getTimerId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTN3270PPackageClasses() {
|
||||
PrinterConfig config = new PrinterConfig("localhost", 23);
|
||||
|
||||
// Telnet3270EP
|
||||
haus.nightmare.lib3270j.tn3270p.Telnet3270EP ep =
|
||||
new haus.nightmare.lib3270j.tn3270p.Telnet3270EP(config);
|
||||
assertNotNull(ep);
|
||||
assertTrue(ep instanceof haus.nightmare.lib3270j.printer.Telnet3270EP);
|
||||
|
||||
// DS3270P
|
||||
haus.nightmare.lib3270j.tn3270p.DS3270P ds =
|
||||
new haus.nightmare.lib3270j.tn3270p.DS3270P(config);
|
||||
assertNotNull(ds);
|
||||
assertTrue(ds instanceof haus.nightmare.lib3270j.printer.DS3270P);
|
||||
|
||||
// PD3270
|
||||
haus.nightmare.lib3270j.tn3270p.PD3270 pd =
|
||||
new haus.nightmare.lib3270j.tn3270p.PD3270(config);
|
||||
assertNotNull(pd);
|
||||
assertTrue(pd instanceof haus.nightmare.lib3270j.printer.PD3270);
|
||||
|
||||
// PrintSCS3270
|
||||
haus.nightmare.lib3270j.tn3270p.PrintSCS3270 scs =
|
||||
new haus.nightmare.lib3270j.tn3270p.PrintSCS3270(config, pd, null);
|
||||
assertNotNull(scs);
|
||||
|
||||
// PrintPS3270
|
||||
haus.nightmare.lib3270j.tn3270p.PrintPS3270 printPs =
|
||||
new haus.nightmare.lib3270j.tn3270p.PrintPS3270(config, pd, null);
|
||||
assertNotNull(printPs);
|
||||
|
||||
// PrintSCS3270DB
|
||||
haus.nightmare.lib3270j.tn3270p.PrintSCS3270DB scsDb =
|
||||
new haus.nightmare.lib3270j.tn3270p.PrintSCS3270DB(config);
|
||||
assertNotNull(scsDb);
|
||||
|
||||
// PrintPS3270DB
|
||||
haus.nightmare.lib3270j.tn3270p.PrintPS3270DB psDb =
|
||||
new haus.nightmare.lib3270j.tn3270p.PrintPS3270DB(config);
|
||||
assertNotNull(psDb);
|
||||
|
||||
// PrinterDefinitionTable & PDT
|
||||
haus.nightmare.lib3270j.tn3270p.PrinterDefinitionTable pdt =
|
||||
new haus.nightmare.lib3270j.tn3270p.PrinterDefinitionTable("TEST", "Desc");
|
||||
assertEquals("TEST", pdt.getName());
|
||||
haus.nightmare.lib3270j.tn3270p.PDT pdtAlias =
|
||||
new haus.nightmare.lib3270j.tn3270p.PDT("ALIAS", "Desc");
|
||||
assertEquals("ALIAS", pdtAlias.getName());
|
||||
|
||||
// Timer & TimerEvent
|
||||
haus.nightmare.lib3270j.tn3270p.Timer timer =
|
||||
new haus.nightmare.lib3270j.tn3270p.Timer(100);
|
||||
assertNotNull(timer);
|
||||
haus.nightmare.lib3270j.tn3270p.TimerEvent event =
|
||||
new haus.nightmare.lib3270j.tn3270p.TimerEvent(timer, "Timer2");
|
||||
assertEquals("Timer2", event.getTimerId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package haus.nightmare.lib3270j.printer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for Phase 7 Timer, TimerListener, and TimerEvent lifecycle and callbacks.
|
||||
*/
|
||||
public class TimerPhase7Test {
|
||||
|
||||
@Test
|
||||
public void testTimerExpirationCallback() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicBoolean fired = new AtomicBoolean(false);
|
||||
|
||||
Timer timer = new Timer(50, new TimerListener() {
|
||||
@Override
|
||||
public void timerExpired(TimerEvent event) {
|
||||
assertEquals("TestTimer", event.getTimerId());
|
||||
assertNotNull(event.getTimer());
|
||||
assertTrue(event.getTimestamp() > 0);
|
||||
fired.set(true);
|
||||
latch.countDown();
|
||||
}
|
||||
}, false, "TestTimer");
|
||||
|
||||
assertEquals(50, timer.getInterval());
|
||||
assertEquals("TestTimer", timer.getTimerId());
|
||||
assertFalse(timer.isRepeating());
|
||||
|
||||
timer.start();
|
||||
assertTrue(timer.isRunning());
|
||||
|
||||
boolean completed = latch.await(500, TimeUnit.MILLISECONDS);
|
||||
assertTrue(completed, "Timer did not fire within expected timeout");
|
||||
assertTrue(fired.get());
|
||||
assertFalse(timer.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimerCancelPreventsFiring() throws InterruptedException {
|
||||
AtomicBoolean fired = new AtomicBoolean(false);
|
||||
|
||||
Timer timer = new Timer(150, new TimerListener() {
|
||||
@Override
|
||||
public void timerExpired(TimerEvent event) {
|
||||
fired.set(true);
|
||||
}
|
||||
});
|
||||
|
||||
timer.start();
|
||||
assertTrue(timer.isRunning());
|
||||
timer.cancel();
|
||||
assertFalse(timer.isRunning());
|
||||
|
||||
Thread.sleep(250);
|
||||
assertFalse(fired.get(), "Cancelled timer should not have fired");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimerRestartAndReset() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
Timer timer = new Timer(80);
|
||||
timer.addTimerListener(new TimerListener() {
|
||||
@Override
|
||||
public void timerExpired(TimerEvent event) {
|
||||
count.incrementAndGet();
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
timer.start();
|
||||
Thread.sleep(30);
|
||||
// Restart resets the countdown
|
||||
timer.restart();
|
||||
assertTrue(timer.isRunning());
|
||||
|
||||
boolean completed = latch.await(300, TimeUnit.MILLISECONDS);
|
||||
assertTrue(completed);
|
||||
assertEquals(1, count.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatingTimer() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(3);
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
Timer timer = new Timer(30, new TimerListener() {
|
||||
@Override
|
||||
public void timerExpired(TimerEvent event) {
|
||||
count.incrementAndGet();
|
||||
latch.countDown();
|
||||
}
|
||||
}, true, "RepeatingTimer");
|
||||
|
||||
assertTrue(timer.isRepeating());
|
||||
timer.start();
|
||||
|
||||
boolean completed = latch.await(500, TimeUnit.MILLISECONDS);
|
||||
timer.stop();
|
||||
assertFalse(timer.isRunning());
|
||||
|
||||
assertTrue(completed);
|
||||
assertTrue(count.get() >= 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimerListenerAddRemove() {
|
||||
Timer timer = new Timer(100);
|
||||
TimerListener listener = new TimerListener() {
|
||||
@Override
|
||||
public void timerExpired(TimerEvent event) {}
|
||||
};
|
||||
|
||||
timer.addTimerListener(listener);
|
||||
timer.removeTimerListener(listener);
|
||||
// Should not throw or fail
|
||||
assertNotNull(timer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package haus.nightmare.lib3270j.screen;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class DynamicScreenBufferTest {
|
||||
|
||||
private final EbcdicTranslator translator = new EbcdicTranslator("037");
|
||||
|
||||
@Test
|
||||
public void testDynamicScreenBufferInitialization() {
|
||||
ScreenBuffer buffer = new ScreenBuffer(24, 80, 62, 160, translator);
|
||||
assertEquals(24, buffer.getDefRows());
|
||||
assertEquals(80, buffer.getDefCols());
|
||||
assertEquals(62, buffer.getAltRows());
|
||||
assertEquals(160, buffer.getAltCols());
|
||||
assertEquals(62, buffer.getMaxRows());
|
||||
assertEquals(160, buffer.getMaxCols());
|
||||
assertEquals(24, buffer.getRows());
|
||||
assertEquals(80, buffer.getCols());
|
||||
assertFalse(buffer.isScreenAlt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicScreenSwitching() {
|
||||
ScreenBuffer buffer = new ScreenBuffer(24, 80, 62, 160, translator);
|
||||
|
||||
// Erase to alternate (62x160)
|
||||
buffer.erase(true);
|
||||
assertTrue(buffer.isScreenAlt());
|
||||
assertEquals(62, buffer.getRows());
|
||||
assertEquals(160, buffer.getCols());
|
||||
|
||||
// Address beyond 4096 (e.g., row 50, col 50 = 50 * 160 + 50 = 8050)
|
||||
int testAddr = 50 * 160 + 50;
|
||||
assertTrue(testAddr > 4096);
|
||||
ExtendedAttribute ea = buffer.getCell(testAddr);
|
||||
assertNotNull(ea);
|
||||
ea.ec = (byte) 0xC1; // EBCDIC 'A'
|
||||
assertEquals((byte) 0xC1, buffer.getCell(testAddr).ec);
|
||||
|
||||
// Erase to default (24x80)
|
||||
buffer.erase(false);
|
||||
assertFalse(buffer.isScreenAlt());
|
||||
assertEquals(24, buffer.getRows());
|
||||
assertEquals(80, buffer.getCols());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicAlternateDimensionsExpansion() {
|
||||
ScreenBuffer buffer = new ScreenBuffer(24, 80, 43, 132, translator);
|
||||
assertEquals(43, buffer.getMaxRows());
|
||||
assertEquals(132, buffer.getMaxCols());
|
||||
|
||||
// Expand to 62x160 dynamically
|
||||
buffer.setAlternateDimensions(62, 160);
|
||||
assertEquals(62, buffer.getAltRows());
|
||||
assertEquals(160, buffer.getAltCols());
|
||||
assertEquals(62, buffer.getMaxRows());
|
||||
assertEquals(160, buffer.getMaxCols());
|
||||
|
||||
buffer.erase(true);
|
||||
assertEquals(62, buffer.getRows());
|
||||
assertEquals(160, buffer.getCols());
|
||||
|
||||
int maxAddr = 62 * 160 - 1; // 9919
|
||||
buffer.getCell(maxAddr).ec = (byte) 0xE9; // 'Z'
|
||||
assertEquals((byte) 0xE9, buffer.getCell(maxAddr).ec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTerminalModelDynamic() {
|
||||
TerminalModel model = TerminalModel.IBM_DYNAMIC;
|
||||
assertTrue(model.isDynamic());
|
||||
assertEquals(0, model.getModelNumber());
|
||||
assertTrue(model.isColor());
|
||||
assertEquals("IBM-DYNAMIC-E", model.getTerminalType());
|
||||
assertEquals("IBM-DYNAMIC", model.getBaseTerminalType());
|
||||
|
||||
ScreenBuffer buffer = new ScreenBuffer(model, translator);
|
||||
assertEquals(24, buffer.getDefRows());
|
||||
assertEquals(80, buffer.getDefCols());
|
||||
assertEquals(62, buffer.getAltRows());
|
||||
assertEquals(160, buffer.getAltCols());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
package haus.nightmare.lib3270j.security;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.ecl.ECLConnection;
|
||||
import haus.nightmare.lib3270j.ecl.ECLSession;
|
||||
import haus.nightmare.lib3270j.security.ssl.HODSSLECLSessionImpl;
|
||||
import haus.nightmare.lib3270j.tls.TlsCertificateVerifier;
|
||||
import haus.nightmare.lib3270j.tls.TlsTrustManager;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Comprehensive test suite for Phase 9: SSL/TLS Security Facade.
|
||||
*/
|
||||
public class Phase9SslSecurityTest {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
private ConnectionConfig config;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
config = new ConnectionConfig("localhost", 992, TerminalModel.IBM_3279_4, true);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.1 Constructors & Property Configuration
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testConstructorsAndProperties() {
|
||||
// Default constructor
|
||||
HODSSLECLSessionImpl ssl1 = new HODSSLECLSessionImpl();
|
||||
assertNotNull(ssl1.getConfig());
|
||||
assertTrue(ssl1.isUseJSSE());
|
||||
assertTrue(ssl1.IsUseJSSE());
|
||||
|
||||
// ConnectionConfig constructor
|
||||
HODSSLECLSessionImpl ssl2 = new HODSSLECLSessionImpl(config);
|
||||
assertSame(config, ssl2.getConfig());
|
||||
assertSame(config, ssl2.GetConfig());
|
||||
|
||||
// ECLSession constructor
|
||||
ECLSession session = new ECLSession("zos.example.com", 992, TerminalModel.IBM_3279_4, true);
|
||||
HODSSLECLSessionImpl ssl3 = new HODSSLECLSessionImpl(session);
|
||||
assertSame(session, ssl3.getSession());
|
||||
assertSame(session, ssl3.GetSession());
|
||||
|
||||
// ECLConnection constructor
|
||||
ECLConnection conn = session.getConnection();
|
||||
HODSSLECLSessionImpl ssl4 = new HODSSLECLSessionImpl(conn);
|
||||
assertSame(conn, ssl4.getConnection());
|
||||
assertSame(conn, ssl4.GetConnection());
|
||||
|
||||
// Properties constructor
|
||||
Properties props = new Properties();
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_SSL_USE_JSSE, "true");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_CERT_NAME, "MyCertAlias");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_CERT_URL, "/path/to/cert.p12");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_CERT_PASSWORD, "certpass");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_TRUSTSTORE, "/path/to/trust.p12");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_TRUSTSTORE_PASSWORD, "trustpass");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_TRUSTSTORE_TYPE, "PKCS12");
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_TLS_VERSION, "TLSv1.3");
|
||||
|
||||
HODSSLECLSessionImpl ssl5 = new HODSSLECLSessionImpl(props);
|
||||
assertEquals("MyCertAlias", ssl5.getCertificateAlias());
|
||||
assertEquals("MyCertAlias", ssl5.GetCertificateAlias());
|
||||
assertEquals("MyCertAlias", ssl5.getCertificateName());
|
||||
assertEquals("MyCertAlias", ssl5.GetCertificateName());
|
||||
assertEquals("/path/to/cert.p12", ssl5.getKeyStorePath());
|
||||
assertEquals("/path/to/cert.p12", ssl5.GetKeyStorePath());
|
||||
assertEquals("certpass", ssl5.getKeyStorePassword());
|
||||
assertEquals("/path/to/trust.p12", ssl5.getTrustStorePath());
|
||||
assertEquals("trustpass", ssl5.getTrustStorePassword());
|
||||
assertEquals("PKCS12", ssl5.getTrustStoreType());
|
||||
assertEquals("TLSv1.3", ssl5.getTLSProtocolVersion());
|
||||
assertEquals("TLSv1.3", ssl5.GetTLSProtocolVersion());
|
||||
|
||||
// Get/Set properties
|
||||
ssl5.setProperty("custom.security.level", "HIGH");
|
||||
assertEquals("HIGH", ssl5.getProperty("custom.security.level"));
|
||||
assertEquals("HIGH", ssl5.GetProperty("custom.security.level"));
|
||||
assertEquals("DEFAULT", ssl5.getProperty("nonexistent", "DEFAULT"));
|
||||
assertEquals("DEFAULT", ssl5.GetProperty("nonexistent", "DEFAULT"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.2 Client Keystore Loading & KeyManager Generation
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testClientKeyStoreLoadingAndKeyManagers() throws Exception {
|
||||
HODSSLECLSessionImpl ssl = new HODSSLECLSessionImpl(config);
|
||||
|
||||
// Generate a test PKCS12 keystore in-memory
|
||||
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||
ks.load(null, "testpwd".toCharArray());
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ks.store(baos, "testpwd".toCharArray());
|
||||
byte[] ksBytes = baos.toByteArray();
|
||||
|
||||
// 1. Test loadKeyStore from InputStream
|
||||
KeyStore loadedKs = ssl.loadKeyStore(new ByteArrayInputStream(ksBytes), "testpwd".toCharArray(), "PKCS12");
|
||||
assertNotNull(loadedKs);
|
||||
assertSame(loadedKs, ssl.getKeyStore());
|
||||
assertSame(loadedKs, ssl.GetKeyStore());
|
||||
|
||||
// 2. Test loadKeyStore from file path
|
||||
File ksFile = tempDir.resolve("client_keystore.p12").toFile();
|
||||
try (FileOutputStream fos = new FileOutputStream(ksFile)) {
|
||||
fos.write(ksBytes);
|
||||
}
|
||||
ssl.setKeyStorePath(ksFile.getAbsolutePath());
|
||||
ssl.setKeyStorePassword("testpwd");
|
||||
ssl.setKeyStoreType("PKCS12");
|
||||
ssl.setCertificateAlias("client_alias");
|
||||
|
||||
assertEquals(ksFile.getAbsolutePath(), ssl.getKeyStorePath());
|
||||
assertEquals("testpwd", ssl.getKeyStorePassword());
|
||||
assertEquals("PKCS12", ssl.getKeyStoreType());
|
||||
assertEquals("client_alias", ssl.getCertificateAlias());
|
||||
|
||||
KeyManager[] kms = ssl.createKeyManagers();
|
||||
assertNotNull(kms);
|
||||
assertTrue(kms.length > 0);
|
||||
assertSame(kms, ssl.getKeyManagers());
|
||||
assertSame(kms, ssl.GetKeyManagers());
|
||||
|
||||
// 3. Test explicit setKeyManagers
|
||||
ssl.setKeyManagers(kms);
|
||||
assertSame(kms, ssl.getKeyManagers());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.3 Truststore & CustomizedCAs Handling
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testTrustStoreAndCustomizedCAs() throws Exception {
|
||||
HODSSLECLSessionImpl ssl = new HODSSLECLSessionImpl(config);
|
||||
|
||||
// Generate in-memory truststore with standard default HoD password "hod"
|
||||
KeyStore ts = KeyStore.getInstance("PKCS12");
|
||||
ts.load(null, "hod".toCharArray());
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ts.store(baos, "hod".toCharArray());
|
||||
byte[] tsBytes = baos.toByteArray();
|
||||
|
||||
// 1. Test loadTrustStore from InputStream
|
||||
KeyStore loadedTs = ssl.loadTrustStore(new ByteArrayInputStream(tsBytes), "hod".toCharArray(), "PKCS12");
|
||||
assertNotNull(loadedTs);
|
||||
assertSame(loadedTs, ssl.getTrustStore());
|
||||
assertSame(loadedTs, ssl.GetTrustStore());
|
||||
|
||||
// 2. Test loadTrustStore from file path
|
||||
File tsFile = tempDir.resolve("custom_truststore.p12").toFile();
|
||||
try (FileOutputStream fos = new FileOutputStream(tsFile)) {
|
||||
fos.write(tsBytes);
|
||||
}
|
||||
ssl.setTrustStorePath(tsFile.getAbsolutePath());
|
||||
ssl.setTrustStorePassword("hod");
|
||||
ssl.setTrustStoreType("PKCS12");
|
||||
|
||||
assertEquals(tsFile.getAbsolutePath(), ssl.getTrustStorePath());
|
||||
assertEquals("hod", ssl.getTrustStorePassword());
|
||||
assertEquals("PKCS12", ssl.getTrustStoreType());
|
||||
|
||||
TrustManager[] tms = ssl.createTrustManagers();
|
||||
assertNotNull(tms);
|
||||
assertEquals(1, tms.length);
|
||||
assertTrue(tms[0] instanceof TlsTrustManager);
|
||||
assertSame(tms, ssl.getTrustManagers());
|
||||
assertSame(tms, ssl.GetTrustManagers());
|
||||
|
||||
// 3. Test CustomizedCAs ClassLoader lookup with default password "hod"
|
||||
ClassLoader mockCl = new ClassLoader(getClass().getClassLoader()) {
|
||||
@Override
|
||||
public java.io.InputStream getResourceAsStream(String name) {
|
||||
if ("CustomizedCAs.p12".equals(name)) {
|
||||
return new ByteArrayInputStream(tsBytes);
|
||||
}
|
||||
return super.getResourceAsStream(name);
|
||||
}
|
||||
};
|
||||
|
||||
HODSSLECLSessionImpl sslCustomCAs = new HODSSLECLSessionImpl();
|
||||
sslCustomCAs.setCustomizedCAsClassLoader(mockCl);
|
||||
assertSame(mockCl, sslCustomCAs.getCustomizedCAsClassLoader());
|
||||
assertSame(mockCl, sslCustomCAs.GetCustomizedCAsClassLoader());
|
||||
|
||||
KeyStore cCAs = sslCustomCAs.loadCustomizedCAs();
|
||||
assertNotNull(cCAs, "Expected loadCustomizedCAs to find CustomizedCAs.p12 via ClassLoader with default password");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.4 TLS Protocol & Cipher Suite Negotiation
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testTlsProtocolAndCipherSuiteNegotiation() throws Exception {
|
||||
HODSSLECLSessionImpl ssl = new HODSSLECLSessionImpl(config);
|
||||
|
||||
// Default enabled protocols (TLSv1.3 and TLSv1.2)
|
||||
String[] defaultProtocols = ssl.getEnabledProtocols();
|
||||
assertNotNull(defaultProtocols);
|
||||
assertTrue(defaultProtocols.length >= 2);
|
||||
assertEquals("TLSv1.3", defaultProtocols[0]);
|
||||
assertEquals("TLSv1.2", defaultProtocols[1]);
|
||||
|
||||
// Supported protocols
|
||||
String[] supportedProtocols = ssl.getSupportedProtocols();
|
||||
assertNotNull(supportedProtocols);
|
||||
assertTrue(supportedProtocols.length > 0);
|
||||
|
||||
// Set explicit TLS protocol version
|
||||
ssl.setTLSProtocolVersion("TLSv1.2");
|
||||
assertEquals("TLSv1.2", ssl.getTLSProtocolVersion());
|
||||
assertEquals("TLSv1.2", ssl.GetTLSProtocolVersion());
|
||||
assertArrayEquals(new String[] { "TLSv1.2" }, ssl.getEnabledProtocols());
|
||||
|
||||
// Set multiple protocols
|
||||
ssl.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" });
|
||||
assertArrayEquals(new String[] { "TLSv1.3", "TLSv1.2" }, ssl.getEnabledProtocols());
|
||||
assertArrayEquals(new String[] { "TLSv1.3", "TLSv1.2" }, ssl.GetEnabledProtocols());
|
||||
|
||||
// Cipher suites
|
||||
String[] supportedCiphers = ssl.getSupportedCipherSuites();
|
||||
assertNotNull(supportedCiphers);
|
||||
assertTrue(supportedCiphers.length > 0);
|
||||
|
||||
String[] testCiphers = new String[] {
|
||||
supportedCiphers[0],
|
||||
supportedCiphers.length > 1 ? supportedCiphers[1] : supportedCiphers[0]
|
||||
};
|
||||
ssl.setEnabledCipherSuites(testCiphers);
|
||||
assertArrayEquals(testCiphers, ssl.getEnabledCipherSuites());
|
||||
assertArrayEquals(testCiphers, ssl.GetEnabledCipherSuites());
|
||||
|
||||
// Test applySocketSettings on dummy SSLSocket
|
||||
SSLContext ctx = ssl.createSSLContext();
|
||||
SSLSocket dummySocket = (SSLSocket) ctx.getSocketFactory().createSocket();
|
||||
ssl.applySocketSettings(dummySocket);
|
||||
assertTrue(dummySocket.getEnabledProtocols().length > 0);
|
||||
dummySocket.close();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.5 SSLContext & Socket Factory Creation
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testSSLContextAndSocketFactory() throws Exception {
|
||||
HODSSLECLSessionImpl ssl = new HODSSLECLSessionImpl(config);
|
||||
ssl.setSecurityProtocol("TLS");
|
||||
assertEquals("TLS", ssl.getSecurityProtocol());
|
||||
assertEquals("TLS", ssl.GetSecurityProtocol());
|
||||
|
||||
SSLContext ctx = ssl.createSSLContext();
|
||||
assertNotNull(ctx);
|
||||
assertSame(ctx, ssl.getSSLContext());
|
||||
assertSame(ctx, ssl.GetSSLContext());
|
||||
|
||||
SSLSocketFactory factory = ssl.getSSLSocketFactory();
|
||||
assertNotNull(factory);
|
||||
assertSame(factory, ssl.GetSSLSocketFactory());
|
||||
|
||||
// initSSLContext alias
|
||||
SSLContext ctx2 = ssl.initSSLContext();
|
||||
assertNotNull(ctx2);
|
||||
|
||||
// Explicit setSSLContext
|
||||
ssl.setSSLContext(ctx);
|
||||
assertSame(ctx, ssl.getSSLContext());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.6 Interactive Certificate Verification & Bypass
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testInteractiveCertificateVerificationAndBypass() throws Exception {
|
||||
HODSSLECLSessionImpl ssl = new HODSSLECLSessionImpl(config);
|
||||
|
||||
// Verification bypass
|
||||
ssl.setTlsVerifyCert(false);
|
||||
assertFalse(ssl.isTlsVerifyCert());
|
||||
assertFalse(ssl.IsTlsVerifyCert());
|
||||
|
||||
TrustManager[] tmsBypass = ssl.createTrustManagers();
|
||||
assertNotNull(tmsBypass);
|
||||
X509TrustManager tm = (X509TrustManager) tmsBypass[0];
|
||||
assertDoesNotThrow(() -> tm.checkServerTrusted(new X509Certificate[0], "RSA"));
|
||||
|
||||
// Verifier callback
|
||||
ssl.setTlsVerifyCert(true);
|
||||
assertTrue(ssl.isTlsVerifyCert());
|
||||
|
||||
AtomicBoolean verifierCalled = new AtomicBoolean(false);
|
||||
TlsCertificateVerifier customVerifier = (chain, authType, exception) -> {
|
||||
verifierCalled.set(true);
|
||||
return true;
|
||||
};
|
||||
ssl.setCertificateVerifier(customVerifier);
|
||||
assertSame(customVerifier, ssl.getCertificateVerifier());
|
||||
assertSame(customVerifier, ssl.GetCertificateVerifier());
|
||||
|
||||
TrustManager[] tmsVerifier = ssl.createTrustManagers();
|
||||
X509TrustManager tm2 = (X509TrustManager) tmsVerifier[0];
|
||||
assertDoesNotThrow(() -> tm2.checkServerTrusted(new X509Certificate[0], "RSA"));
|
||||
assertTrue(verifierCalled.get(), "Expected verifier callback to be invoked");
|
||||
|
||||
// Verifier rejection
|
||||
ssl.setCertificateVerifier(TlsCertificateVerifier.REJECT_ALL);
|
||||
TrustManager[] tmsReject = ssl.createTrustManagers();
|
||||
X509TrustManager tmReject = (X509TrustManager) tmsReject[0];
|
||||
assertThrows(CertificateException.class, () -> tmReject.checkServerTrusted(new X509Certificate[0], "RSA"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.7 Drop-in Canonical Facade (haus.nightmare.lib3270j.eNetwork.security.ssl)
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testCanonicalDropInFacade() {
|
||||
haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl facade =
|
||||
new haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl();
|
||||
assertNotNull(facade);
|
||||
assertTrue(facade instanceof HODSSLECLSessionImpl);
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl facadeConfig =
|
||||
new haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl(config);
|
||||
assertSame(config, facadeConfig.getConfig());
|
||||
|
||||
ECLSession session = new ECLSession();
|
||||
haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl facadeSession =
|
||||
new haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl(session);
|
||||
assertSame(session, facadeSession.getSession());
|
||||
|
||||
ECLConnection conn = session.getConnection();
|
||||
haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl facadeConn =
|
||||
new haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl(conn);
|
||||
assertSame(conn, facadeConn.getConnection());
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty(HODSSLECLSessionImpl.SESSION_CERT_NAME, "CanonicalCert");
|
||||
haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl facadeProps =
|
||||
new haus.nightmare.lib3270j.eNetwork.security.ssl.HODSSLECLSessionImpl(props);
|
||||
assertEquals("CanonicalCert", facadeProps.getCertificateAlias());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.8 ECLSession & ECLConnection Integration
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testECLSessionAndConnectionIntegration() {
|
||||
ECLSession session = new ECLSession("mainframe.corp.com", 992, TerminalModel.IBM_3279_4, true);
|
||||
assertNotNull(session.getSSLSessionImpl());
|
||||
assertSame(session.getSSLSessionImpl(), session.GetSSLSessionImpl());
|
||||
|
||||
ECLConnection connection = session.getConnection();
|
||||
assertNotNull(connection.getSSLSessionImpl());
|
||||
assertSame(connection.getSSLSessionImpl(), connection.GetSSLSessionImpl());
|
||||
|
||||
// Synchronizing properties
|
||||
connection.setCertificateName("PROD_KEY");
|
||||
assertEquals("PROD_KEY", connection.getCertificateName());
|
||||
assertEquals("PROD_KEY", session.getClient().getConfig().getKeyStoreAlias());
|
||||
|
||||
connection.setCertificateURL("/keystores/client.p12");
|
||||
assertEquals("/keystores/client.p12", connection.getCertificateURL());
|
||||
assertEquals("/keystores/client.p12", session.getClient().getConfig().getKeyStorePath());
|
||||
|
||||
connection.setCertificatePassword("prodsecret");
|
||||
assertEquals("prodsecret", connection.getCertificatePassword());
|
||||
assertEquals("prodsecret", session.getClient().getConfig().getKeyStorePassword());
|
||||
|
||||
connection.setJSSETrustStore("/keystores/trust.p12");
|
||||
assertEquals("/keystores/trust.p12", connection.getJSSETrustStore());
|
||||
assertEquals("/keystores/trust.p12", session.getClient().getConfig().getTrustStorePath());
|
||||
|
||||
connection.setJSSETrustStorePassword("trustsecret");
|
||||
assertEquals("trustsecret", connection.getJSSETrustStorePassword());
|
||||
assertEquals("trustsecret", session.getClient().getConfig().getTrustStorePassword());
|
||||
|
||||
connection.setJSSETrustStoreType("PKCS12");
|
||||
assertEquals("PKCS12", connection.getJSSETrustStoreType());
|
||||
assertEquals("PKCS12", session.getClient().getConfig().getTrustStoreType());
|
||||
|
||||
connection.setTLSProtocolVersion("TLSv1.3");
|
||||
assertEquals("TLSv1.3", connection.getTLSProtocolVersion());
|
||||
assertEquals("TLSv1.3", session.getClient().getConfig().getSslProtocol());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 9.9 Security Status & Key Strength
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
public void testSecurityStatusAndKeyStrength() {
|
||||
HODSSLECLSessionImpl ssl = new HODSSLECLSessionImpl();
|
||||
|
||||
// When inactive
|
||||
assertNull(ssl.getSSLSession());
|
||||
assertNull(ssl.GetSSLSession());
|
||||
assertEquals(0, ssl.getKeyStrength());
|
||||
assertEquals(0, ssl.GetKeyStrength());
|
||||
assertEquals("NONE", ssl.getCipherSuite());
|
||||
assertEquals("NONE", ssl.GetCipherSuite());
|
||||
assertEquals(0, ssl.getPeerCertificates().length);
|
||||
assertEquals(0, ssl.GetPeerCertificates().length);
|
||||
assertEquals(0, ssl.getLocalCertificates().length);
|
||||
assertEquals(0, ssl.GetLocalCertificates().length);
|
||||
assertFalse(ssl.isAuthenticated());
|
||||
assertFalse(ssl.IsAuthenticated());
|
||||
assertFalse(ssl.isConnected());
|
||||
assertFalse(ssl.IsConnected());
|
||||
|
||||
// Reset and close
|
||||
assertDoesNotThrow(ssl::close);
|
||||
assertDoesNotThrow(ssl::Close);
|
||||
assertDoesNotThrow(ssl::reset);
|
||||
assertDoesNotThrow(ssl::Reset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package haus.nightmare.lib3270j.telnet;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.datastream.DataStreamProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicTelnetFSMTest {
|
||||
|
||||
private ConnectionConfig config;
|
||||
private ScreenBuffer screenBuffer;
|
||||
private DataStreamProcessor dsProcessor;
|
||||
private TelnetFSM fsm;
|
||||
private MockConnection connection;
|
||||
|
||||
private static class MockConnection extends TelnetConnection {
|
||||
final List<byte[]> sentData = new ArrayList<>();
|
||||
|
||||
MockConnection(ConnectionConfig config, TelnetFSM fsm) {
|
||||
super(config, fsm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void sendRaw(byte[] data) {
|
||||
sentData.add(data.clone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void sendRaw(byte[] data, int offset, int length) {
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(data, offset, b, 0, length);
|
||||
sentData.add(b);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
config = new ConnectionConfig("localhost", 23, TerminalModel.IBM_DYNAMIC);
|
||||
config.setDynamicDimensions(62, 160);
|
||||
screenBuffer = new ScreenBuffer(24, 80, 62, 160, new EbcdicTranslator());
|
||||
dsProcessor = new DataStreamProcessor(screenBuffer, new EbcdicTranslator());
|
||||
fsm = new TelnetFSM(config, screenBuffer, dsProcessor);
|
||||
connection = new MockConnection(config, fsm);
|
||||
fsm.setConnection(connection);
|
||||
}
|
||||
|
||||
private void feedBytes(int... bytes) {
|
||||
for (int b : bytes) {
|
||||
fsm.feedByte(b & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCandidateTerminalTypesIncludesDynamic() {
|
||||
// Feed DO TERMINAL-TYPE: IAC DO TTYPE
|
||||
feedBytes(255, 253, 24);
|
||||
|
||||
// Host requests TTYPE: IAC SB TTYPE SEND IAC SE
|
||||
feedBytes(255, 250, 24, 1, 255, 240);
|
||||
|
||||
// Verify sent response is IBM-DYNAMIC-E
|
||||
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"),
|
||||
"Expected terminal type negotiation to send IBM-DYNAMIC, got: " + s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindWithQuerySSIZE03SetsDynamicDimensions() {
|
||||
// Simulate TN3270E BIND packet with SSIZE = 0x03
|
||||
// EH_SIZE = 5 bytes header + BIND parameters (at least 25 bytes)
|
||||
byte[] bindData = new byte[5 + 26];
|
||||
// BIND_OFF_RD = 20, BIND_OFF_CD = 21, BIND_OFF_RA = 22, BIND_OFF_CA = 23, BIND_OFF_SSIZE = 24
|
||||
bindData[5 + 20] = 24; // Primary rows
|
||||
bindData[5 + 21] = 80; // Primary cols
|
||||
bindData[5 + 22] = 0; // Alt rows
|
||||
bindData[5 + 23] = 0; // Alt cols
|
||||
bindData[5 + 24] = 0x03; // SSIZE = 0x03 (Query)
|
||||
|
||||
fsm.process_bind(bindData, 0, 1);
|
||||
|
||||
assertEquals(62, screenBuffer.getAltRows());
|
||||
assertEquals(160, screenBuffer.getAltCols());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindWithExplicitSSIZE7FSetsCustomDimensions() {
|
||||
// Simulate TN3270E BIND packet with SSIZE = 0x7F and custom 50x132 dimensions
|
||||
byte[] bindData = new byte[5 + 26];
|
||||
bindData[5 + 20] = 24; // Primary rows
|
||||
bindData[5 + 21] = 80; // Primary cols
|
||||
bindData[5 + 22] = 50; // Alt rows
|
||||
bindData[5 + 23] = (byte) 132; // Alt cols
|
||||
bindData[5 + 24] = 0x7F; // SSIZE = 0x7F (Explicit)
|
||||
|
||||
fsm.process_bind(bindData, 0, 1);
|
||||
|
||||
assertEquals(50, screenBuffer.getAltRows());
|
||||
assertEquals(132, screenBuffer.getAltCols());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHostStringWithDynamicPrefix() {
|
||||
ConnectionConfig cfg = ConnectionConfig.parseHostString("dynamic:mainframe.example.com:23", 23, null);
|
||||
assertTrue(cfg.isDynamicModel());
|
||||
assertEquals(62, cfg.getDynamicRows());
|
||||
assertEquals(160, cfg.getDynamicCols());
|
||||
|
||||
ConnectionConfig cfgCustom = ConnectionConfig.parseHostString("dynamic[43x132]:mainframe.example.com:23", 23, null);
|
||||
assertTrue(cfgCustom.isDynamicModel());
|
||||
assertEquals(43, cfgCustom.getDynamicRows());
|
||||
assertEquals(132, cfgCustom.getDynamicCols());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
package haus.nightmare.lib3270j.tn3270;
|
||||
|
||||
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.datastream.DataStreamProcessor;
|
||||
import haus.nightmare.lib3270j.ecl.ECLPS;
|
||||
import haus.nightmare.lib3270j.ecl.ECLSession;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import haus.nightmare.lib3270j.telnet.TelnetFSM;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class Phase3ProtocolEngineTest {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private EbcdicTranslator translator;
|
||||
private DataStreamProcessor dsp;
|
||||
private InputProcessor inputProcessor;
|
||||
private TelnetFSM fsm;
|
||||
private ECLSession session;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||
dsp = new DataStreamProcessor(screen, translator);
|
||||
fsm = new TelnetFSM(new haus.nightmare.lib3270j.ConnectionConfig("localhost", 23), screen, dsp);
|
||||
inputProcessor = new InputProcessor(screen, translator, fsm);
|
||||
dsp.setInputProcessor(inputProcessor);
|
||||
session = new ECLSession("localhost", 23, TerminalModel.IBM_3278_2);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 3.1 com.ibm.eNetwork.ECL.tn3270.DS3270
|
||||
// ==========================================
|
||||
|
||||
@Test
|
||||
public void testDS3270ConstantsAndInstantiation() {
|
||||
DS3270 ds = new DS3270(dsp);
|
||||
assertNotNull(ds.getDataStreamProcessor());
|
||||
|
||||
// Verify command constants match HoD specifications
|
||||
assertEquals(1, DS3270.CMD_W);
|
||||
assertEquals(5, DS3270.CMD_EW);
|
||||
assertEquals(13, DS3270.CMD_EWA);
|
||||
assertEquals(2, DS3270.CMD_RB);
|
||||
assertEquals(6, DS3270.CMD_RM);
|
||||
assertEquals(14, DS3270.CMD_RMA);
|
||||
assertEquals(15, DS3270.CMD_EAU);
|
||||
assertEquals(17, DS3270.CMD_WSF);
|
||||
|
||||
assertEquals(241, DS3270.SNA_CMD_W);
|
||||
assertEquals(245, DS3270.SNA_CMD_EW);
|
||||
assertEquals(126, DS3270.SNA_CMD_EWA);
|
||||
assertEquals(242, DS3270.SNA_CMD_RB);
|
||||
assertEquals(246, DS3270.SNA_CMD_RM);
|
||||
assertEquals(110, DS3270.SNA_CMD_RMA);
|
||||
assertEquals(111, DS3270.SNA_CMD_EAU);
|
||||
assertEquals(243, DS3270.SNA_CMD_WSF);
|
||||
|
||||
// Verify orders
|
||||
assertEquals(29, DS3270.ORD_SF);
|
||||
assertEquals(41, DS3270.ORD_SFE);
|
||||
assertEquals(17, DS3270.ORD_SBA);
|
||||
assertEquals(40, DS3270.ORD_SA);
|
||||
assertEquals(44, DS3270.ORD_MF);
|
||||
assertEquals(19, DS3270.ORD_IC);
|
||||
assertEquals(5, DS3270.ORD_PT);
|
||||
assertEquals(60, DS3270.ORD_RA);
|
||||
assertEquals(18, DS3270.ORD_EUA);
|
||||
assertEquals(8, DS3270.ORD_GE);
|
||||
|
||||
// Verify default query lists
|
||||
assertEquals(21, ds.qreplylist.length);
|
||||
assertEquals(21, ds.queryEquiv.length);
|
||||
assertEquals(64, ds.attr_conversion_table.length);
|
||||
|
||||
// Verify drop-in eNetwork facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270.DS3270 facade =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270.DS3270(dsp);
|
||||
assertNotNull(facade.getDataStreamProcessor());
|
||||
assertTrue(facade instanceof DS3270);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDS3270OrderExecution() {
|
||||
DS3270 ds = new DS3270(dsp);
|
||||
screen.erase(false);
|
||||
|
||||
// SBA to pos 80 (Row 1 Col 0)
|
||||
ds.processSBA(80);
|
||||
assertEquals(80, screen.getBufferAddress());
|
||||
|
||||
// SF unprotected field
|
||||
ds.processSF((byte) FA_PRINTABLE);
|
||||
assertTrue(screen.isFormatted());
|
||||
assertTrue(screen.getCell(80).isFieldAttribute());
|
||||
assertEquals(81, screen.getBufferAddress());
|
||||
|
||||
// IC (Insert Cursor)
|
||||
ds.processIC();
|
||||
assertEquals(81, screen.getCursorAddress());
|
||||
|
||||
// SA foreground red
|
||||
ds.processSA(XA_FOREGROUND, 0xF2);
|
||||
|
||||
// RA fill with 'A' (EBCDIC 0xC1) up to pos 90
|
||||
ds.processRA(90, 0xC1);
|
||||
assertEquals(90, screen.getBufferAddress());
|
||||
assertEquals('A', screen.getCell(81).ucs4);
|
||||
assertEquals('A', screen.getCell(89).ucs4);
|
||||
|
||||
// EUA (Erase Unprotected to Address) up to pos 85
|
||||
ds.processSBA(81);
|
||||
ds.processEUA(85);
|
||||
assertEquals(0, screen.getCell(81).ucs4);
|
||||
assertEquals(0, screen.getCell(84).ucs4);
|
||||
assertEquals('A', screen.getCell(85).ucs4);
|
||||
|
||||
// GE (Graphic Escape)
|
||||
ds.processSBA(95);
|
||||
ds.processGE(0xC1);
|
||||
assertEquals(96, screen.getBufferAddress());
|
||||
|
||||
// WCC alarm, restore, reset MDT
|
||||
inputProcessor.setKeyboardLocked(true);
|
||||
ds.processWCC((short) (DS3270.WCC_RESTORE | DS3270.WCC_RMDT | DS3270.WCC_ALARM));
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDS3270ShortBufferProcessingAndSendAid() {
|
||||
AtomicReference<byte[]> sentData = new AtomicReference<>();
|
||||
dsp.setOutputSender(sentData::set);
|
||||
|
||||
DS3270 ds = new DS3270(dsp);
|
||||
|
||||
// Build a short[] stream: Write + WCC + SBA(0, 0) + 'H', 'E', 'L', 'L', 'O'
|
||||
short[] stream = new short[] {
|
||||
DS3270.SNA_CMD_EW,
|
||||
(short) (DS3270.WCC_RESET | DS3270.WCC_RESTORE),
|
||||
DS3270.ORD_SBA, 0x40, 0x40, // Address 0
|
||||
0xC8, 0xC5, 0xD3, 0xD3, 0xD6 // EBCDIC "HELLO"
|
||||
};
|
||||
ds.processData(stream, 0, stream.length);
|
||||
|
||||
assertEquals('H', screen.getCell(0).ucs4);
|
||||
assertEquals('E', screen.getCell(1).ucs4);
|
||||
assertEquals('L', screen.getCell(2).ucs4);
|
||||
assertEquals('L', screen.getCell(3).ucs4);
|
||||
assertEquals('O', screen.getCell(4).ucs4);
|
||||
|
||||
// sendAid
|
||||
ds.sendAid((short) AID_ENTER, 50);
|
||||
assertNotNull(sentData.get());
|
||||
assertTrue(sentData.get().length > 0);
|
||||
assertEquals((byte) AID_ENTER, sentData.get()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQrElemAndQueryReplyObject() {
|
||||
qr_elem elem = new qr_elem(qr_elem.TYPE_COLOR, qr_elem.SEND_YES);
|
||||
assertEquals(qr_elem.TYPE_COLOR, elem.getType());
|
||||
assertEquals(qr_elem.SEND_YES, elem.getSendFlag());
|
||||
assertTrue(elem.isSendEnabled());
|
||||
|
||||
elem.setSendEnabled(false);
|
||||
assertFalse(elem.isSendEnabled());
|
||||
assertEquals(qr_elem.SEND_NO, elem.getSendFlag());
|
||||
|
||||
// Drop-in eNetwork facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270.qr_elem facadeElem =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270.qr_elem(qr_elem.TYPE_DDM, qr_elem.SEND_YES);
|
||||
assertTrue(facadeElem instanceof qr_elem);
|
||||
assertEquals(qr_elem.TYPE_DDM, facadeElem.getType());
|
||||
|
||||
// DS3270.QueryReplyObject
|
||||
DS3270 ds = new DS3270(dsp);
|
||||
DS3270.QueryReplyObject qro = ds.new QueryReplyObject();
|
||||
qro.addReply("\u0000\u0005\u0081\u0086\u0000");
|
||||
byte[] bytes = qro.getBytes();
|
||||
assertNotNull(bytes);
|
||||
assertTrue(bytes.length > 0);
|
||||
|
||||
// qr_update and qr_set
|
||||
qr_elem[] list = new qr_elem[] {
|
||||
new qr_elem(129, 0),
|
||||
new qr_elem(134, 0)
|
||||
};
|
||||
ds.qr_update(list, (char) 134, (short) 1, 2);
|
||||
assertEquals(1, list[1].getSendFlag());
|
||||
|
||||
ds.qr_set(list, (short) 1, 2);
|
||||
assertEquals(1, list[0].getSendFlag());
|
||||
assertEquals(1, list[1].getSendFlag());
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 3.2 com.ibm.eNetwork.ECL.tn3270.PS3270
|
||||
// ==========================================
|
||||
|
||||
@Test
|
||||
public void testPS3270KeystrokesAndLocking() {
|
||||
PS3270 ps = new PS3270(screen, inputProcessor, translator);
|
||||
screen.erase(false);
|
||||
screen.setCursorAddress(0);
|
||||
|
||||
// Printable char key down
|
||||
boolean handled = ps.keyDown('A', false);
|
||||
assertTrue(handled);
|
||||
assertEquals('A', screen.getCell(0).ucs4);
|
||||
|
||||
// CharKeyStrokes direct
|
||||
ps.CharKeyStrokes('B');
|
||||
assertEquals('B', screen.getCell(1).ucs4);
|
||||
|
||||
// Keyboard locking
|
||||
ps.lockKeyboard(7); // TWAIT
|
||||
assertTrue(ps.islocked_TWAIT());
|
||||
assertTrue(inputProcessor.isKeyboardLocked());
|
||||
|
||||
// Keystroke ignored when locked
|
||||
boolean lockedIgnored = ps.keyDown('C', false);
|
||||
assertFalse(lockedIgnored);
|
||||
assertEquals(0, screen.getCell(2).ucs4);
|
||||
|
||||
// Unlock with Escape key (27)
|
||||
boolean unlocked = ps.keyDown(27, false);
|
||||
assertTrue(unlocked);
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
|
||||
// Special EAB manipulation
|
||||
ps.SetSpecialEAB(10, (byte) 0x55);
|
||||
assertEquals((byte) 0x55, ps.GetSpecialEAB(10));
|
||||
assertEquals((byte) 0x55, screen.getSpecialEAB(10));
|
||||
|
||||
// LU-LU Session flag
|
||||
assertFalse(ps.is_LULU_Session());
|
||||
ps.set_LULU_Session(true);
|
||||
assertTrue(ps.is_LULU_Session());
|
||||
|
||||
// Drop-in eNetwork facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270.PS3270 facadePs =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270.PS3270(screen, inputProcessor, translator);
|
||||
assertTrue(facadePs instanceof PS3270);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPS3270DBCSInputChar() {
|
||||
PS3270 ps = new PS3270(screen, inputProcessor, translator);
|
||||
screen.erase(false);
|
||||
|
||||
// Place unprotected field at 0
|
||||
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||
screen.setCursorAddress(1);
|
||||
|
||||
int res = ps.DBCSinputChar('X', 1);
|
||||
assertEquals(1, res);
|
||||
assertEquals('X', screen.getCell(1).ucs4);
|
||||
|
||||
// On protected field, DBCSinputChar should return 0
|
||||
screen.setFieldAttribute(10, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
int resProt = ps.DBCSinputChar('Y', 11);
|
||||
assertEquals(0, resProt);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 3.3 com.ibm.eNetwork.ECL.tn3270.Telnet3270E
|
||||
// ==========================================
|
||||
|
||||
@Test
|
||||
public void testTelnet3270EConstantsAndFunctions() {
|
||||
Telnet3270E telnet = new Telnet3270E(fsm, null);
|
||||
|
||||
assertEquals(40, Telnet3270E.TELOPT_TN3270E);
|
||||
assertEquals(0, Telnet3270E.TN3270E_ASSOCIATE);
|
||||
assertEquals(1, Telnet3270E.TN3270E_CONNECT);
|
||||
assertEquals(2, Telnet3270E.TN3270E_DEVICE_TYPE);
|
||||
assertEquals(3, Telnet3270E.TN3270E_FUNCTIONS);
|
||||
assertEquals(4, Telnet3270E.TN3270E_IS);
|
||||
assertEquals(7, Telnet3270E.TN3270E_REQUEST);
|
||||
|
||||
assertEquals(0, Telnet3270E.DATA_TYPE_3270_DATA);
|
||||
assertEquals(1, Telnet3270E.DATA_TYPE_SCS_DATA);
|
||||
assertEquals(2, Telnet3270E.DATA_TYPE_RESPONSE);
|
||||
assertEquals(3, Telnet3270E.DATA_TYPE_BIND_IMAGE);
|
||||
assertEquals(4, Telnet3270E.DATA_TYPE_UNBIND);
|
||||
assertEquals(5, Telnet3270E.DATA_TYPE_NVT_DATA);
|
||||
assertEquals(7, Telnet3270E.DATA_TYPE_SSCP_LU_DATA);
|
||||
|
||||
assertEquals(fsm, telnet.getFSM());
|
||||
|
||||
// Drop-in eNetwork facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270.Telnet3270E facadeTelnet =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270.Telnet3270E(fsm, null);
|
||||
assertTrue(facadeTelnet instanceof Telnet3270E);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 3.4 com.ibm.eNetwork.ECL.tn3270.NVT3270
|
||||
// ==========================================
|
||||
|
||||
@Test
|
||||
public void testNVT3270OutboundFramingAndAddress() {
|
||||
DS3270 ds = new DS3270(dsp);
|
||||
ECLPS ps = new ECLPS(screen, inputProcessor, translator);
|
||||
NVT3270 nvt = new NVT3270("test-host", null, ps, ds);
|
||||
|
||||
assertEquals(ds, nvt.getDS());
|
||||
assertEquals(ps, nvt.getPS());
|
||||
|
||||
// Address derivation at cursor 258 (0x0102 -> high 1, low 2)
|
||||
screen.setCursorAddress(258);
|
||||
NVT3270 nvt2 = new NVT3270("test-host", null, ps, ds);
|
||||
assertEquals(1, nvt2.address1st());
|
||||
assertEquals(2, nvt2.address2nd());
|
||||
|
||||
// process_outbound short array
|
||||
short[] outbound = new short[] { 0xC1, 0xC2 };
|
||||
nvt.process_outbound(outbound, 0, outbound.length);
|
||||
assertTrue(ps.isNVTmode());
|
||||
|
||||
// Drop-in eNetwork facade
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.tn3270.NVT3270 facadeNvt =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.tn3270.NVT3270("host", null, ps, ds);
|
||||
assertTrue(facadeNvt instanceof NVT3270);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 3.5 ECLSession Integration
|
||||
// ==========================================
|
||||
|
||||
@Test
|
||||
public void testECLSessionIntegrationAccessors() {
|
||||
assertNotNull(session.GetDS());
|
||||
assertNotNull(session.getDS());
|
||||
assertNotNull(session.GetTelnet());
|
||||
assertNotNull(session.getTelnet());
|
||||
assertNotNull(session.GetPS3270());
|
||||
assertNotNull(session.getPS3270());
|
||||
|
||||
assertTrue(session.getDS() instanceof DS3270);
|
||||
assertTrue(session.getTelnet() instanceof Telnet3270E);
|
||||
assertTrue(session.getPS3270() instanceof PS3270);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
package haus.nightmare.lib3270j.xfer;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
import haus.nightmare.lib3270j.charset.Cp037;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig.AllocationUnit;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig.CrAction;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig.ExistAction;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig.HostType;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig.RecordFormat;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig.TransferMode;
|
||||
import haus.nightmare.lib3270j.ft.dir.HostDirectoryEntry;
|
||||
import haus.nightmare.lib3270j.xfer3270.Xfer3270;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Verification test suite for Phase 6: File Transfer Subsystem & Interface Compliance.
|
||||
*/
|
||||
public class Phase6FileTransferTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test FileTransferInterface Constants and Name Transformations")
|
||||
public void testFileTransferInterfaceAndNaming() {
|
||||
Xfer3270 xfer = new Xfer3270();
|
||||
|
||||
// 1. Constants verification
|
||||
assertEquals(0, FileTransferInterface.VM_CMS);
|
||||
assertEquals(1, FileTransferInterface.MVS_TSO);
|
||||
assertEquals(2, FileTransferInterface.CICS);
|
||||
assertEquals(3, FileTransferInterface.OS400);
|
||||
assertEquals(0, FileTransferInterface.ASCII);
|
||||
assertEquals(1, FileTransferInterface.BINARY);
|
||||
assertEquals(0, FileTransferInterface.GET);
|
||||
assertEquals(1, FileTransferInterface.PUT);
|
||||
|
||||
// 2. VM/CMS host and local filename formatting
|
||||
assertEquals("test file", xfer.getHostFileName("test.file", FileTransferInterface.VM_CMS));
|
||||
assertEquals("TEST FILE", xfer.getHostFileName("TEST.FILE", FileTransferInterface.VM_CMS));
|
||||
assertEquals("longfile extensio", xfer.getHostFileName("longfilename.extensionlong", FileTransferInterface.VM_CMS));
|
||||
assertEquals("NONE NONE", xfer.getHostFileName("", FileTransferInterface.VM_CMS));
|
||||
assertEquals("MYFILE.DATA", xfer.getLocalFileName("MYFILE DATA", FileTransferInterface.VM_CMS));
|
||||
|
||||
// 3. MVS/TSO host and local dataset formatting
|
||||
assertEquals("USER.TEST.DATA", xfer.getHostFileName("USER.TEST.DATA", FileTransferInterface.MVS_TSO));
|
||||
assertEquals("user.test.data", xfer.getHostFileName("user.test.data", FileTransferInterface.MVS_TSO));
|
||||
assertEquals("QUALIFIE.DATASET", xfer.getHostFileName("QUALIFIERVERYLONG.DATASET", FileTransferInterface.MVS_TSO));
|
||||
assertEquals("MY.DATASET.NAME", xfer.getLocalFileName("'MY.DATASET.NAME'", FileTransferInterface.MVS_TSO));
|
||||
assertEquals("UNQUOTED.DS", xfer.getLocalFileName("UNQUOTED.DS", FileTransferInterface.MVS_TSO));
|
||||
|
||||
// 4. CICS file formatting
|
||||
assertEquals("cicsfile", xfer.getHostFileName("cicsfile", FileTransferInterface.CICS));
|
||||
assertEquals("CICSFILE", xfer.getHostFileName("CICSFILE", FileTransferInterface.CICS));
|
||||
assertEquals("TOOLONGF", xfer.getHostFileName("TOOLONGFILENAME", FileTransferInterface.CICS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test FileTransferFileObject Mainframe Dataset Encapsulation")
|
||||
public void testFileTransferFileObject() {
|
||||
// Standard HoD constructor
|
||||
FileTransferFileObject fo1 = new FileTransferFileObject("MYFILE", 1024L, false);
|
||||
assertEquals("MYFILE", fo1.getName());
|
||||
assertEquals(1024L, fo1.getSize());
|
||||
assertFalse(fo1.isDirectory());
|
||||
assertEquals("MYFILE:1024:false", fo1.toString());
|
||||
|
||||
// Extended dataset configuration
|
||||
FileTransferFileObject fo2 = new FileTransferFileObject("test.txt", "USER.SRC.COBOL(PGM1)");
|
||||
assertEquals("test.txt", fo2.getLocalFile());
|
||||
assertEquals("USER.SRC.COBOL(PGM1)", fo2.getHostDatasetName());
|
||||
fo2.setHostType(HostType.TSO);
|
||||
fo2.setTransferMode(TransferMode.ASCII);
|
||||
fo2.setCrAction(CrAction.REMOVE);
|
||||
fo2.setExistAction(ExistAction.REPLACE);
|
||||
fo2.setRecfm(RecordFormat.FIXED);
|
||||
fo2.setLrecl(80);
|
||||
fo2.setBlksize(3120);
|
||||
fo2.setPrimarySpace(10);
|
||||
fo2.setSecondarySpace(5);
|
||||
fo2.setSpaceUnits(AllocationUnit.TRACKS);
|
||||
|
||||
// Convert to FTConfig
|
||||
FTConfig config = fo2.toFTConfig();
|
||||
assertEquals("USER.SRC.COBOL(PGM1)", config.getHostFilename());
|
||||
assertEquals("test.txt", config.getLocalFilename());
|
||||
assertEquals(HostType.TSO, config.getHostType());
|
||||
assertEquals(TransferMode.ASCII, config.getTransferMode());
|
||||
assertEquals(RecordFormat.FIXED, config.getRecfm());
|
||||
assertEquals(80, config.getLrecl());
|
||||
assertEquals(3120, config.getBlksize());
|
||||
assertEquals(10, config.getPrimarySpace());
|
||||
assertEquals(5, config.getSecondarySpace());
|
||||
assertEquals(AllocationUnit.TRACKS, config.getUnits());
|
||||
assertTrue(config.isOverwrite());
|
||||
|
||||
// Round-trip back from FTConfig
|
||||
FileTransferFileObject fo3 = new FileTransferFileObject(config);
|
||||
assertEquals("USER.SRC.COBOL(PGM1)", fo3.getHostDatasetName());
|
||||
assertEquals("test.txt", fo3.getLocalFile());
|
||||
assertEquals(RecordFormat.FIXED, fo3.getRecfm());
|
||||
assertEquals(80, fo3.getLrecl());
|
||||
assertEquals(3120, fo3.getBlksize());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test FileTransferStatusInterface Callbacks")
|
||||
public void testFileTransferStatusInterface() {
|
||||
List<String> events = new ArrayList<>();
|
||||
long[] progressBytes = new long[1];
|
||||
int[] progressPct = new int[1];
|
||||
|
||||
FileTransferStatusInterface status = new FileTransferStatusInterface() {
|
||||
@Override
|
||||
public void setFileInfo(String fileName, long fileSize) {
|
||||
events.add("INFO:" + fileName + ":" + fileSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTransfer() {
|
||||
events.add("START");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bytesTransfered(long bytes) {
|
||||
progressBytes[0] = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesTransferred, long totalBytes, int percent) {
|
||||
progressBytes[0] = bytesTransferred;
|
||||
progressPct[0] = percent;
|
||||
events.add("PROGRESS:" + bytesTransferred + "/" + totalBytes + " (" + percent + "%)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferComplete() {
|
||||
events.add("COMPLETE");
|
||||
}
|
||||
};
|
||||
|
||||
status.setFileInfo("TEST.DAT", 1000L);
|
||||
status.startTransfer();
|
||||
status.onProgress(500L, 1000L, 50);
|
||||
status.transferComplete();
|
||||
|
||||
assertEquals(4, events.size());
|
||||
assertEquals("INFO:TEST.DAT:1000", events.get(0));
|
||||
assertEquals("START", events.get(1));
|
||||
assertEquals("PROGRESS:500/1000 (50%)", events.get(2));
|
||||
assertEquals("COMPLETE", events.get(3));
|
||||
assertEquals(500L, progressBytes[0]);
|
||||
assertEquals(50, progressPct[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Xfer3270 setOptionFlags Comprehensive Parsing")
|
||||
public void testXfer3270OptionFlagsParsing() {
|
||||
Xfer3270 xfer = new Xfer3270();
|
||||
|
||||
// 1. Standard options
|
||||
xfer.setOptionFlags("ASCII CRLF RECFM(F) LRECL(80) BLKSIZE(3120) SPACE(10,5) TRACKS REPLACE BUFFERSIZE(4096)");
|
||||
assertTrue(xfer.option_ASCII);
|
||||
assertTrue(xfer.option_CRLF);
|
||||
assertFalse(xfer.option_APPEND);
|
||||
assertFalse(xfer.option_UNICODE);
|
||||
assertEquals(4096, xfer.GetMTUSize());
|
||||
|
||||
// 2. UNICODE UTF-8 options
|
||||
xfer.setOptionFlags("BINARY NOCRLF UNICODE(UTF-8) APPEND CYLINDERS SPACE(50,20) LRECL(133)");
|
||||
assertFalse(xfer.option_ASCII);
|
||||
assertFalse(xfer.option_CRLF);
|
||||
assertTrue(xfer.option_APPEND);
|
||||
assertTrue(xfer.option_UNICODE);
|
||||
assertEquals(Xfer3270.UNICODE_UTF8, xfer.unicodeType);
|
||||
|
||||
// 3. UNICODE UCS-2 options
|
||||
xfer.setOptionFlags("ASCII UNICODE(UCS2) RECFM(V) LRECL(256) AVBLOCK(1024) SPACE(100)");
|
||||
assertTrue(xfer.option_ASCII);
|
||||
assertTrue(xfer.option_UNICODE);
|
||||
assertEquals(Xfer3270.UNICODE_UCS2, xfer.unicodeType);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Unicode Streams (XferFileOutputUnicode and XferFileInputUnicode)")
|
||||
public void testUnicodeStreams(@TempDir Path tempDir) throws Exception {
|
||||
File targetFile = tempDir.resolve("unicode_test.txt").toFile();
|
||||
Cp037 cp = new Cp037();
|
||||
|
||||
// 1. Output stream: write host EBCDIC bytes to local UTF-8 file with BOM
|
||||
XferFileOutputUnicode outStream = new XferFileOutputUnicode(
|
||||
targetFile, false, new byte[]{13, 10}, true, cp,
|
||||
XferUnicodeConverter.UNICODE_UTF8, false, false);
|
||||
|
||||
// Host bytes: "HELLO" in EBCDIC 037 = [0xC8, 0xC5, 0xD3, 0xD3, 0xD6]
|
||||
byte[] ebcdicHello = new byte[]{(byte) 0xC8, (byte) 0xC5, (byte) 0xD3, (byte) 0xD3, (byte) 0xD6};
|
||||
outStream.write(ebcdicHello, 0, ebcdicHello.length);
|
||||
outStream.close();
|
||||
|
||||
// Verify written file contains UTF-8 BOM followed by "HELLO"
|
||||
byte[] writtenBytes = Files.readAllBytes(targetFile.toPath());
|
||||
assertTrue(writtenBytes.length >= 8); // 3-byte BOM + 5 bytes "HELLO"
|
||||
assertEquals((byte) 0xEF, writtenBytes[0]);
|
||||
assertEquals((byte) 0xBB, writtenBytes[1]);
|
||||
assertEquals((byte) 0xBF, writtenBytes[2]);
|
||||
assertEquals('H', (char) writtenBytes[3]);
|
||||
assertEquals('E', (char) writtenBytes[4]);
|
||||
assertEquals('L', (char) writtenBytes[5]);
|
||||
assertEquals('L', (char) writtenBytes[6]);
|
||||
assertEquals('O', (char) writtenBytes[7]);
|
||||
|
||||
// 2. Input stream: read UTF-8 file with BOM, strip BOM, and translate back to host EBCDIC
|
||||
XferFileInputUnicode inStream = new XferFileInputUnicode(
|
||||
targetFile, new byte[]{13, 10}, cp,
|
||||
XferUnicodeConverter.UNICODE_UTF8, 1, false);
|
||||
|
||||
byte[] hostBuffer = new byte[32];
|
||||
int read = inStream.readData(hostBuffer, 0, hostBuffer.length);
|
||||
inStream.close();
|
||||
|
||||
assertEquals(5, read);
|
||||
assertEquals((byte) 0xC8, hostBuffer[0]);
|
||||
assertEquals((byte) 0xC5, hostBuffer[1]);
|
||||
assertEquals((byte) 0xD3, hostBuffer[2]);
|
||||
assertEquals((byte) 0xD3, hostBuffer[3]);
|
||||
assertEquals((byte) 0xD6, hostBuffer[4]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Directory Retrieval with FileTransferHostDirectoryInterface")
|
||||
public void testDirectoryRetrieval() throws Exception {
|
||||
Xfer3270 xfer = new Xfer3270();
|
||||
|
||||
// Sample VM/CMS FILELIST response
|
||||
String cmsQuery = "PROFILE EXEC A1 F 80 12 1 2026-08-20 12:00:00\n" +
|
||||
"NOTE TXT A1 V 133 5 1 2026-08-21 14:30:00";
|
||||
|
||||
Vector<String> localFiles = new Vector<>();
|
||||
Vector<String> hostFiles = new Vector<>();
|
||||
final int[] reportedStatus = new int[]{-1};
|
||||
final int[] entryCount = new int[]{0};
|
||||
|
||||
FileTransferHostDirectoryInterface callback = new FileTransferHostDirectoryInterface() {
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
reportedStatus[0] = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDirectoryLoaded(List<? extends HostDirectoryEntry> entries) {
|
||||
entryCount[0] = entries.size();
|
||||
}
|
||||
};
|
||||
|
||||
Vector result = xfer.getFiles(cmsQuery, FileTransferInterface.VM_CMS, 30, localFiles, hostFiles, callback);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(2, hostFiles.size());
|
||||
assertEquals(2, localFiles.size());
|
||||
assertEquals("PROFILE EXEC", hostFiles.get(0));
|
||||
assertEquals("PROFILE.EXEC", localFiles.get(0));
|
||||
assertEquals("NOTE TXT", hostFiles.get(1));
|
||||
assertEquals("NOTE.TXT", localFiles.get(1));
|
||||
assertEquals(FileTransferHostDirectoryInterface.STATUS_OK, reportedStatus[0]);
|
||||
assertEquals(2, entryCount[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Drop-In Canonical Package Exposure (eNetwork.ECL.xfer.*)")
|
||||
public void testCanonicalPackageFacades() {
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.xfer.FileTransferFileObject eFileObj =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.xfer.FileTransferFileObject("DATASET.NAME", 5000L);
|
||||
assertEquals("DATASET.NAME", eFileObj.getName());
|
||||
assertEquals(5000L, eFileObj.getSize());
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.xfer3270.Xfer3270 eXfer =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.xfer3270.Xfer3270();
|
||||
assertNotNull(eXfer);
|
||||
assertEquals("Idle", eXfer.stateIs(0));
|
||||
assertEquals("TEST.TXT", eXfer.getLocalFileName("'TEST.TXT'", FileTransferInterface.MVS_TSO));
|
||||
|
||||
haus.nightmare.lib3270j.eNetwork.ECL.xfer3270.CMSPrintXfer ePrint =
|
||||
new haus.nightmare.lib3270j.eNetwork.ECL.xfer3270.CMSPrintXfer(eXfer);
|
||||
assertNotNull(ePrint);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user