package io.termd.core.term;
import io.termd.core.util.Helper;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class TermInfoTest {
/**
* Compare two terminal escape sequences.
*
* @param expected the expected sequence
* @param actual the sequence
*/
public static void assertSequenceEquals(String expected, String actual) {
try {
assertEquals(expected, actual);
} catch (AssertionError e) {
throw new AssertionError("Was expecting sequence <" + Helper.escape(actual) + "> to be equals to <"
+ Helper.escape(expected) + ">");
}
}
@Test
public void testParseComment() throws Exception {
TermInfoParser parser = new TermInfoParser("#\n");
parser.parseCommentLine();
}
@Test
public void testParseHeaderLine() throws Exception {
assertParseHeaderLine("foo,\n", "foo");
assertParseHeaderLine("foo bar,\n", "foo bar");
assertParseHeaderLine("foo,bar,\n", "foo,bar");
assertParseHeaderLine("foo|bar,\n", "foo", "bar");
assertParseHeaderLine("foo|bar juu,\n", "foo", "bar juu");
assertParseHeaderLine("foo|bar,juu,\n", "foo", "bar,juu");
assertParseHeaderLine("foo|bar|juu,\n", "foo", "bar", "juu");
failParseHeaderLine("");
failParseHeaderLine("foo,");
failParseHeaderLine("foo|");
failParseHeaderLine("foo|,");
}
private void assertParseHeaderLine(String s, String... expected) {
try {
TermInfoParser parser = new TermInfoParser(s);
List<String> actual = parser.parseHeaderLine();
assertEquals(Arrays.asList(expected), actual);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
private void failParseHeaderLine(String s) {
try {
TermInfoParser parser = new TermInfoParser(s);
parser.parseHeaderLine();
fail();
} catch (ParseException ignore) {
} catch (TokenMgrError ignore) {
}
}
@Test
public void testParseFeatureLine() throws Exception {
assertFeatureLine(" foo=bar\\\\,\n", Feature.create("foo", new Sequence("bar\\")));
assertFeatureLine(" foo,\n", Feature.create("foo", true));
assertFeatureLine(" foo@,\n", Feature.create("foo", false));
assertFeatureLine(" foo,bar,\n", Feature.create("foo", true), Feature.create("bar", true));
assertFeatureLine(" foo, bar,\n", Feature.create("foo", true), Feature.create("bar", true));
assertFeatureLine(" foo=,\n", Feature.create("foo", new Sequence(Collections.<OpCode>emptyList())));
assertFeatureLine(" foo=\\s,\n", Feature.create("foo", new Sequence(" ")));
assertFeatureLine(" foo=\\\\,\n", Feature.create("foo", new Sequence("\\")));
assertFeatureLine(" foo=\\,,\n", Feature.create("foo", new Sequence(",")));
assertFeatureLine(" foo=\\^,\n", Feature.create("foo", new Sequence("^")));
assertFeatureLine(" foo=^\\,\n", Feature.create("foo", new Sequence(Character.toString((char) 28))));
assertFeatureLine(" foo=^^,\n", Feature.create("foo", new Sequence(Character.toString((char) 30))));
assertFeatureLine(" foo=bar,\n", Feature.create("foo", new Sequence("bar")));
assertFeatureLine(" foo=bar,juu=daa,\n", Feature.create("foo", new Sequence("bar")), Feature.create("juu", new Sequence("daa")));
assertFeatureLine(" foo=bar, juu=daa,\n", Feature.create("foo", new Sequence("bar")), Feature.create("juu", new Sequence("daa")));
assertFeatureLine(" foo,bar=juu,\n", Feature.create("foo", true), Feature.create("bar", new Sequence("juu")));
assertFeatureLine(" foo=bar,juu,\n", Feature.create("foo", new Sequence("bar")), Feature.create("juu", true));
assertFeatureLine(" foo=b\\,ar,\n", Feature.create("foo", new Sequence("b,ar")));
assertFeatureLine(" foo#1234,\n", Feature.create("foo", 1234));
assertFeatureLine(" foo#0,\n", Feature.create("foo", 0));
assertFeatureLine(" foo#0x1234,\n", Feature.create("foo", 0x1234));
assertFeatureLine(" foo#01234,\n", Feature.create("foo", 01234));
failParseFeatureLine(" ");
failParseFeatureLine(" foo,");
failParseFeatureLine(" foo#,\n");
failParseFeatureLine(" foo#a,\n");
}
@Test
public void testStringParameterized() {
String[] tests = {
// Parameters
"%p1",
"%p2",
"%p3",
"%p4",
"%p5",
"%p6",
"%p7",
"%p7",
"%p8",
"%p9",
// Printf
"%d",
"%o",
"%x",
"%X",
"%s",
"%+d",
"%#d",
"% d",
//
// "%{1}",
};
/*
for (int i = 0;i < tests.length;i += 1) {
String info = " " + Capability.acs_chars.name + "=" + tests[i] + ",\n";
assertParseFeatureLine(info);
}
*/
}
@Test
public void testOpParam() {
assertParseInstr("%p1", new OpCode.PushParam(1));
assertParseInstr("%p2", new OpCode.PushParam(2));
assertParseInstr("%p3", new OpCode.PushParam(3));
assertParseInstr("%p4", new OpCode.PushParam(4));
assertParseInstr("%p5", new OpCode.PushParam(5));
assertParseInstr("%p6", new OpCode.PushParam(6));
assertParseInstr("%p7", new OpCode.PushParam(7));
assertParseInstr("%p8", new OpCode.PushParam(8));
assertParseInstr("%p9", new OpCode.PushParam(9));
}
@Test
public void testOpIntegerConstant() {
assertParseInstr("%{0}", new OpCode.PushConstant(0, true));
assertParseInstr("%{1}", new OpCode.PushConstant(1, true));
assertParseInstr("%{01}", new OpCode.PushConstant(1, true));
assertParseInstr("%{2}", new OpCode.PushConstant(2, true));
assertParseInstr("%{10}", new OpCode.PushConstant(10, true));
assertParseInstr("%{11}", new OpCode.PushConstant(11, true));
}
@Test
public void testOpPrintf() throws ParseException {
// Specifier
assertParseOpPrintf("%d", null, null, null, 'd');
assertParseOpPrintf("%o", null, null, null, 'o');
assertParseOpPrintf("%x", null, null, null, 'x');
assertParseOpPrintf("%X", null, null, null, 'X');
// Flags
assertParseOpPrintf("%#", '#', null, null, null);
assertParseOpPrintf("% ", ' ', null, null, null);
assertParseOpPrintf("%:#", '#', null, null, null);
assertParseOpPrintf("%:-", '-', null, null, null);
assertParseOpPrintf("%:+", '+', null, null, null);
assertParseOpPrintf("%: ", ' ', null, null, null);
// Width
assertParseOpPrintf("%0", null, "0", null, null);
assertParseOpPrintf("%10", null, "10", null, null);
// Precision
assertParseOpPrintf("%.0", null, null, "0", null);
assertParseOpPrintf("%.10", null, null, "10", null);
// Various
assertParseOpPrintf("% 10", ' ', "10", null, null);
assertParseOpPrintf("%:-16s", '-', "16", null, 's');
assertParseOpPrintf("%:-16.16s", '-', "16", "16", 's');
assertParseOpPrintf("%03d", null, "03", null, 'd');
}
@Test
public void testOpVariable() {
assertParseInstr("%Pa", new OpCode.SetPopVar('a'));
assertParseInstr("%Pz", new OpCode.SetPopVar('z'));
assertParseInstr("%PA", new OpCode.SetPopVar('A'));
assertParseInstr("%PZ", new OpCode.SetPopVar('Z'));
assertParseInstr("%ga", new OpCode.GetPushVar('a'));
assertParseInstr("%gz", new OpCode.GetPushVar('z'));
assertParseInstr("%gA", new OpCode.GetPushVar('A'));
assertParseInstr("%gZ", new OpCode.GetPushVar('Z'));
}
@Test
public void testOpBit() {
assertParseInstr("%&", OpCode.Bit.AND);
assertParseInstr("%|", OpCode.Bit.OR);
assertParseInstr("%^", OpCode.Bit.XOR);
assertParseInstr("%~", OpCode.Bit.NEG);
}
@Test
public void testOpLogical() {
assertParseInstr("%=", OpCode.Logical.EQ);
assertParseInstr("%>", OpCode.Logical.GT);
assertParseInstr("%<", OpCode.Logical.LT);
assertParseInstr("%A", OpCode.Logical.AND);
assertParseInstr("%O", OpCode.Logical.OR);
assertParseInstr("%!", OpCode.Logical.NEG);
}
@Test
public void testOpArithmetic() {
assertParseInstr("%+", OpCode.Arithmetic.PLUS);
assertParseInstr("%-", OpCode.Arithmetic.MINUS);
assertParseInstr("%*", OpCode.Arithmetic.MUL);
assertParseInstr("%/", OpCode.Arithmetic.DIV);
assertParseInstr("%m", OpCode.Arithmetic.MOD);
}
@Test
public void testOpAdd1ToParams() {
assertParseInstr("%i", OpCode.Add1ToParams.INSTANCE);
}
@Test
public void testIfThenElse() {
List<OpCode> p1 = Arrays.<OpCode>asList(new OpCode.PushParam(1));
List<OpCode> p2 = Arrays.<OpCode>asList(new OpCode.PushParam(2));
List<OpCode> p3 = Arrays.<OpCode>asList(new OpCode.PushParam(3));
List<OpCode> p4 = Arrays.<OpCode>asList(new OpCode.PushParam(4));
List<OpCode> p5 = Arrays.<OpCode>asList(new OpCode.PushParam(5));
List<OpCode> p6 = Arrays.<OpCode>asList(new OpCode.PushParam(6));
List<OpCode> p7 = Arrays.<OpCode>asList(new OpCode.PushParam(7));
assertParseInstr("%?%p1%t%p2%;", new OpCode.If(p1, new OpCode.Then(p2)));
assertParseInstr("%?%p1%t%p2%e%p3%;", new OpCode.If(p1, new OpCode.Then(p2, new OpCode.Else(p3))));
assertParseInstr("%?%p1%t%p2%e%p3%t%p4%;", new OpCode.If(p1, new OpCode.Then(p2, new OpCode.If(p3, new OpCode.Then(p4)))));
assertParseInstr("%?%p1%t%p2%e%p3%t%p4%e%p5%;", new OpCode.If(p1, new OpCode.Then(p2, new OpCode.If(p3, new OpCode.Then(p4, new OpCode.Else(p5))))));
assertParseInstr("%?%p1%t%p2%e%p3%t%p4%e%p5%t%p6%;", new OpCode.If(p1, new OpCode.Then(p2, new OpCode.If(p3, new OpCode.Then(p4, new OpCode.If(p5, new OpCode.Then(p6)))))));
assertParseInstr("%?%p1%t%p2%e%p3%t%p4%e%p5%t%p6%e%p7%;", new OpCode.If(p1, new OpCode.Then(p2, new OpCode.If(p3, new OpCode.Then(p4, new OpCode.If(p5, new OpCode.Then(p6, new OpCode.Else(p7))))))));
List<OpCode> p1p2 = Arrays.<OpCode>asList(new OpCode.PushParam(1), new OpCode.PushParam(2));
List<OpCode> p3p4 = Arrays.<OpCode>asList(new OpCode.PushParam(3), new OpCode.PushParam(4));
List<OpCode> p5p6 = Arrays.<OpCode>asList(new OpCode.PushParam(5), new OpCode.PushParam(6));
assertParseInstr("%?%p1%p2%t%p3%p4%e%p5%p6%;", new OpCode.If(p1p2, new OpCode.Then(p3p4, new OpCode.Else(p5p6))));
}
@Test
public void testOpEsc() {
assertParseInstr("%%", OpCode.Percent.INSTANCE);
}
@Test
public void testOpStrLen() {
assertParseInstr("%l", OpCode.PushStrLen.INSTANCE);
}
@Test
public void testOpPushConstant() {
assertParseInstr("%'a'", new OpCode.PushConstant('a', false));
assertParseInstr("%'\''", new OpCode.PushConstant('\'', false));
assertParseInstr("%'\\123'", new OpCode.PushConstant((char) 83, false));
assertParseInstr("%'\\0'", new OpCode.PushConstant(0, false));
}
@Test
public void testOpPrintPop() {
assertParseInstr("%c", OpCode.PrintChar.INSTANCE);
assertParseInstr("%s", new OpCode.Printf(null, null, null, 's'));
}
private void assertParseOpPrintf(String s, Character flag, String width, String precision, Character specifier) {
assertParseInstr(s, new OpCode.Printf(flag, width, precision, specifier));
}
private void assertParseInstr(String s, OpCode expected) {
try {
OpCode op = new TermInfoParser(s).parseSingleOpCode();
assertEquals(expected, op);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
@Test
public void testStringSpecialCharsFeature() {
String[] tests = {
"\\a", String.valueOf((char) 7),
"\\A", String.valueOf((char) 1),
"\\b", String.valueOf((char) 8),
"\\e", String.valueOf((char) 27),
"\\E", String.valueOf((char) 27),
"\\f", String.valueOf((char) 12),
"\\n", String.valueOf('\n'),
"\\r", String.valueOf('\r'),
"\\s", String.valueOf(' '),
"\\t", String.valueOf('\t'),
"\\^", String.valueOf('^'),
"\\\\", String.valueOf('\\'),
"\\:", String.valueOf(':'),
"\\0", String.valueOf((char) 0),
"\\030", String.valueOf((char) 24),
"\\101", String.valueOf((char) 65),
"\\01ab", String.valueOf((char) 0) + "1ab",
"^c", String.valueOf((char)('C' - 64)),
};
for (int i = 0;i < tests.length;i += 2) {
String info = " " + Capability.acs_chars.name + "=" + tests[i] + ",\n";
assertFeatureLine(info, Feature.create(Capability.acs_chars.name, new Sequence(tests[i + 1])));
}
}
private void assertFeatureLine(String s, Feature<?>... expected) {
List<Feature<?>> features = assertParseFeatureLine(s);
assertEquals(Arrays.asList(expected), features);
}
private List<Feature<?>> assertParseFeatureLine(String s) {
try {
final List<Feature<?>> features = new ArrayList<>();
TermInfoParser parser = new TermInfoParser(s);
ParserHandler handler = new ParserHandler() {
@Override
public void addBooleanFeature(String name, boolean value) {
features.add(Feature.create(name, value));
}
@Override
public void addStringFeature(String name, Sequence value) {
features.add(Feature.create(name, value));
}
@Override
public void addNumericFeature(String name, int value) {
features.add(Feature.create(name, value));
}
};
parser.parseFeatureLine(handler);
return features;
} catch (ParseException e) {
throw new AssertionError(e);
}
}
private void failParseFeatureLine(String s) {
try {
TermInfoParser parser = new TermInfoParser(s);
parser.parseFeatureLine();
fail();
} catch (ParseException ignore) {
} catch (TokenMgrError ignore) {
}
}
@Test
public void testParseDevice() {
assertParseDevice("abc|def,\n foo,bar,\n");
}
private void assertParseDevice(String s) {
try {
TermInfoParser parser = new TermInfoParser(s);
parser.parseDevice();
} catch (ParseException e) {
throw new AssertionError(e);
}
}
@Test
public void testParseDatabase() {
assertParseDatabase("" +
"# hello \n" +
"abc|def,\n" +
" foo,bar,\n" +
"juu|daa,\n" +
" bilto|bilta,\n"
);
}
private void assertParseDatabase(String s) {
try {
TermInfoParser parser = new TermInfoParser(s);
parser.parseDatabase();
} catch (ParseException e) {
throw new AssertionError(e);
}
}
@Test
public void testUse() {
TermInfo info = assertBuildDevices(
"a,\n" +
" bw,\n" +
"b,\n" +
" use=a,\n" +
"c,\n" +
" use=a,bw@,\n"
);
assertTrue(info.getDevice("a").getFeature(Capability.auto_left_margin));
assertTrue(info.getDevice("b").getFeature(Capability.auto_left_margin));
assertFalse(info.getDevice("c").getFeature(Capability.auto_left_margin));
}
@Test
public void testUseCycle() throws Exception {
TermInfoBuilder builder = new TermInfoBuilder();
new TermInfoParser("" +
"a,\n" +
" use=b,\n" +
"b,\n" +
" use=c,\n" +
"c,\n" +
" use=a,\n"
).parseDatabase(builder);
try {
builder.build();
fail();
} catch (IllegalStateException ignore) {
}
}
@Test
public void testUseNotFound() throws Exception {
TermInfoBuilder builder = new TermInfoBuilder();
new TermInfoParser("" +
"a,\n" +
" use=b,\n"
).parseDatabase(builder);
try {
builder.build();
fail();
} catch (IllegalStateException ignore) {
}
}
private TermInfo assertBuildDevices(String s) {
try {
TermInfoParser parser = new TermInfoParser(s);
TermInfoBuilder builder = new TermInfoBuilder();
parser.parseDatabase(builder);
return builder.build();
} catch (ParseException e) {
throw new AssertionError(e);
}
}
@Test
public void testParse() throws Exception {
ByteArrayOutputStream res = new ByteArrayOutputStream();
try (InputStream in = TermInfoParser.class.getResourceAsStream("terminfo.src")) {
byte[] buffer = new byte[256];
while (true) {
int len = in.read(buffer);
if (len == -1) {
break;
}
res.write(buffer, 0, len);
}
}
String s = res.toString("ISO-8859-1");
TermInfoParser parser = new TermInfoParser(s);
TermInfoBuilder builder = new TermInfoBuilder();
parser.parseDatabase(builder);
TermInfo info = builder.build();
Collection<Device> entries = info.devices();
assertEquals(2645, entries.size());
Device device = info.getDevice("xterm-color");
assertNotNull(device);
}
@Test
public void testSomeEval() throws Exception {
Sequence cup = TermInfo.defaultInfo().getDevice("xterm-color").getFeature(Capability.cursor_address);
Sequence smcup = TermInfo.defaultInfo().getDevice("xterm-color").getFeature(Capability.enter_ca_mode);
assertSequenceEquals("\033[1;1H", cup.eval("0", "0"));
assertSequenceEquals("\0337\033[?47h", smcup.eval());
Sequence setb = TermInfo.defaultInfo().getDevice("xtermc").getFeature(Capability.set_background);
assertSequenceEquals("\033[44m", setb.eval("1"));
assertSequenceEquals("\033[42m", setb.eval("2"));
assertSequenceEquals("\033[46m", setb.eval("3"));
assertSequenceEquals("\033[41m", setb.eval("4"));
assertSequenceEquals("\033[45m", setb.eval("5"));
assertSequenceEquals("\033[43m", setb.eval("6"));
assertSequenceEquals("\033[47m", setb.eval("7"));
assertSequenceEquals("\033[412m", setb.eval("12"));
Sequence pfkey = TermInfo.defaultInfo().getDevice("tvi924").getFeature(Capability.pkey_key);
assertEquals("\033|2foo\031", pfkey.eval("1", "foo"));
}
@Test
public void testEvalPushParam() throws Exception {
for (int i = 0;i < 9;i++) {
OpCode.PushParam opcode = new OpCode.PushParam(1 + i);
String[] parameters = new String[1 + i];
parameters[i] = "10";
StringBuilder result = new StringBuilder();
EvalContext context = new EvalContext(parameters, result);
opcode.eval(context);
assertEquals(Arrays.asList("10"), context.stack);
assertEquals("", result.toString());
}
}
@Test
public void testEvalPushParamInvalidIndex() throws Exception {
for (int index : Arrays.asList(0, 10)) {
try {
new OpCode.PushParam(index);
fail();
} catch (IllegalArgumentException ignore) {
}
}
}
@Test
public void testEvalPushParamNoParam() throws Exception {
StringBuilder result = new StringBuilder();
EvalContext context = new EvalContext(new String[0], result);
OpCode.PushParam opcode = new OpCode.PushParam(1);
try {
opcode.eval(context);
fail();
} catch (IllegalArgumentException ignore) {
assertEquals(Arrays.<String>asList(), context.stack);
assertEquals("", result.toString());
}
}
@Test
public void testEvalPushConstant() throws Exception {
OpCode.PushConstant opcode = new OpCode.PushConstant(65, false);
StringBuilder result = new StringBuilder();
EvalContext context = new EvalContext(new String[0], result);
opcode.eval(context);
assertEquals(Arrays.asList("A"), context.stack);
assertEquals("", result.toString());
}
@Test
public void testEvalLiteral() throws Exception {
for (String test : Arrays.asList("a", "\r\n", "\033")) {
OpCode.Literal opcode = new OpCode.Literal(test);
StringBuilder result = new StringBuilder();
EvalContext context = new EvalContext(new String[0], result);
opcode.eval(context);
assertEquals(Arrays.<String>asList(), context.stack);
assertEquals(test, result.toString());
}
}
@Test
public void testEvalArithmeticPlus() {
EvalContext context = new EvalContext(new String[0], cp -> {}).push("0").push("0");
OpCode.Arithmetic.PLUS.eval(context);
assertEquals("0", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("0").push("10");
OpCode.Arithmetic.PLUS.eval(context);
assertEquals("10", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("10").push("0");
OpCode.Arithmetic.PLUS.eval(context);
assertEquals("10", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("10").push("5");
OpCode.Arithmetic.PLUS.eval(context);
assertEquals("15", context.pop());
}
@Test
public void testEvalLogicalEq() {
EvalContext context = new EvalContext(new String[0], cp -> {}).push("0").push("0");
OpCode.Logical.EQ.eval(context);
assertEquals("1", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("0").push("1");
OpCode.Logical.EQ.eval(context);
assertEquals("0", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("1").push("1");
OpCode.Logical.EQ.eval(context);
assertEquals("1", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("150").push("150");
OpCode.Logical.EQ.eval(context);
assertEquals("1", context.pop());
context = new EvalContext(new String[0], cp -> {}).push("150").push("4");
OpCode.Logical.EQ.eval(context);
assertEquals("0", context.pop());
}
@Test
public void testEvalIfThenElse() throws Exception {
String[][] tests = {
{ "%?%{0}%tA%;", "" },
{ "%?%{1}%tA%;", "A" },
{ "%?%{0}%tA%eB%;", "B" },
{ "%?%{1}%tA%eB%;", "A" },
{ "%?%{0}%tA%e%{1}%tB%;", "B" },
{ "%?%{1}%tA%e%{1}%tB%;", "A" },
{ "%?%{0}%tA%e%{0}%tB%;", "" },
{ "%?%{1}%tA%e%{0}%tB%;", "A" },
{ "%?%{0}%tA%e%{1}%tB%eC%;", "B" },
{ "%?%{1}%tA%e%{1}%tB%eC%;", "A" },
{ "%?%{0}%tA%e%{0}%tB%eC%;", "C" },
{ "%?%{1}%tA%e%{0}%tB%eC%;", "A" },
};
for (String[] test : tests) {
StringBuilder result = new StringBuilder();
OpCode opCode = new TermInfoParser(test[0]).parseSingleOpCode();
opCode.eval(new EvalContext(new String[0], result)) ;
assertEquals(test[1], result.toString());
}
}
@Test
public void testEvalPrintChar() throws Exception {
StringBuilder result = new StringBuilder();
EvalContext context = new EvalContext(new String[0], result).push("65");
OpCode.PrintChar.INSTANCE.eval(context);
assertEquals("A", result.toString());
}
@Test
public void testEvalPrintf() throws Exception {
StringBuilder result = new StringBuilder();
EvalContext context = new EvalContext(new String[0], result).push("hello");
new OpCode.Printf(null, null, null, 's').eval(context);
assertEquals("hello", result.toString());
}
}