Usermode/GUI Terminal - Fixed title support, added a unit test
authorJohn Hodge <[email protected]>
Sat, 7 Dec 2013 10:16:17 +0000 (18:16 +0800)
committerJohn Hodge <[email protected]>
Sat, 7 Dec 2013 10:16:17 +0000 (18:16 +0800)
Usermode/Applications/gui_terminal_src/UTEST/Makefile [new file with mode: 0644]
Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c [new file with mode: 0644]
Usermode/Applications/gui_terminal_src/UTEST/test_common.c [new file with mode: 0644]
Usermode/Applications/gui_terminal_src/UTEST/test_common.h [new file with mode: 0644]
Usermode/Applications/gui_terminal_src/vt100.c

diff --git a/Usermode/Applications/gui_terminal_src/UTEST/Makefile b/Usermode/Applications/gui_terminal_src/UTEST/Makefile
new file mode 100644 (file)
index 0000000..0aa5213
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#
+#
+
+CPPFLAGS := -I ../include -I ../../../Libraries/ld-acess.so_src/include_exp
+CFLAGS := -Wall -std=c99
+CFLAGS += $(CPPFLAGS)
+TESTFILES := vt100
+
+OBJDIR := stage/obj/
+BINDIR := stage/
+
+.PHONY: all clean
+# run $(TESTFILES:%=run_%)
+
+all: $(TESTFILES:%=$(BINDIR)TEST_%)
+
+#run: all $(TESTFILES:%=run_%)
+
+.PRECIOUS:
+
+#run_%: $(BINDIR)TEST_%
+#      ./$(BINDIR)TEST_%
+
+$(BINDIR)TEST_%: $(OBJDIR)TEST_%.o $(OBJDIR)__%.o $(OBJDIR)test_common.o
+       @mkdir -p $(dir $@)
+       @echo [CC] -o $@
+       @$(CC) -o $@ $(OBJDIR)TEST_$*.o $(OBJDIR)__$*.o $(OBJDIR)test_common.o $(LDFLAGS)
+
+$(OBJDIR)%.o: %.c Makefile
+       @mkdir -p $(dir $@)
+       @echo [CC] -c $< -o $@
+       @$(CC) -c $< -o $@ $(CFLAGS)
+$(OBJDIR)__%.o: ../%.c Makefile
+       @mkdir -p $(dir $@)
+       @echo [CC] -c $< -o $@
+       @$(CC) -c $< -o $@ $(CFLAGS)
+
+
diff --git a/Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c b/Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c
new file mode 100644 (file)
index 0000000..1d06fe8
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ */
+
+#include "test_common.h"
+#include <string.h>
+#include <display.h>
+
+// === GLOBALS ===
+void   (*gpTest_VT100_AddText)(size_t Length, const char *UTF8Text);
+void   (*gpTest_VT100_SetTitle)(const char *Title);
+
+// === CODE ===
+int Test_VT100_Setup(void)
+{
+       gpTest_VT100_AddText = NULL;
+       gpTest_VT100_SetTitle = NULL;
+       return 0;
+}
+
+int Test_VT100_int_Command(int MaxCalls, const char *Command)
+{
+       const char *buf = Command;
+        int    len = strlen(Command);
+        int    nCalls = 0;
+       while( len > 0 )
+       {
+               TEST_ASSERT_R(nCalls, <, MaxCalls);
+               int rv = Term_HandleVT100(NULL, len, buf);
+               if( rv < 0 )
+                       return buf - Command;
+               TEST_ASSERT_R(rv, !=, 0);
+               TEST_ASSERT_R(rv, <=, len);
+               len -= rv;
+               buf += rv;
+               nCalls ++;
+       }
+       
+       return buf - Command;
+}
+
+void Test_VT100_TitleSet(void)
+{
+       #define TITLE1  "Testing Title, quite long and complex ;;"
+       const char cmd_long_long[] = "\033]0;"TITLE1"\033\\";
+       #define TITLE2  "Another completely[]different title--"
+       const char cmd_long_st[] = "\033]0;"TITLE2"\x9c";
+       #define TITLE3  "12345678b sbuog egu nz vlj=+~`][]\033  "
+       const char cmd_long_bel[] = "\033]0;"TITLE3"\007";
+       const char *expected_title;
+
+        int    title_was_set=0, rv;
+       
+       void _settitle(const char *Title) {
+               TEST_ASSERT_SR( Title, ==, expected_title );
+               title_was_set = 1;
+       }
+       gpTest_VT100_SetTitle = _settitle;
+       
+       expected_title = TITLE1;
+       title_was_set = 0;
+       rv = Test_VT100_int_Command(10, cmd_long_long);
+       TEST_ASSERT_R(rv, ==, sizeof(cmd_long_long)-1);
+       TEST_ASSERT(title_was_set);
+       
+       expected_title = TITLE2;
+       title_was_set = 0;
+       rv = Test_VT100_int_Command(10, cmd_long_st);
+       TEST_ASSERT_R(rv, ==, sizeof(cmd_long_st)-1);
+       TEST_ASSERT(title_was_set);
+       
+       expected_title = TITLE3;
+       title_was_set = 0;
+       rv = Test_VT100_int_Command(10, cmd_long_bel);
+       TEST_ASSERT_R(rv, ==, sizeof(cmd_long_bel)-1);
+       TEST_ASSERT(title_was_set);
+}
+
+// === TESTS ===
+tTEST  gaTests[] = {
+       {"VT100 TitleSet", NULL, Test_VT100_TitleSet, NULL}
+};
+int giNumTests = sizeof(gaTests)/sizeof(gaTests[0]);
+
+// === Hooks ===
+void   *gpTermStatePtr;
+void *Display_GetTermState(tTerminal *Term) {
+       return gpTermStatePtr;
+}
+void Display_SetTermState(tTerminal *Term, void *State) {
+       gpTermStatePtr = State;
+}
+
+void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text) {
+       TEST_ASSERT(gpTest_VT100_AddText);
+       gpTest_VT100_AddText(Length, UTF8Text);
+}
+void Display_Newline(tTerminal *Term, bool bCarriageReturn) {
+       //if( gpTest_VT100_Newline )
+       //      gpTest_VT100_Newline(Term, bCarriageReturn);
+}
+void Display_SetScrollArea(tTerminal *Term, int Start, int Count) {
+       //if( gpTest_VT100_SetScrollArea )
+       //      gpTest_VT100_SetScrollArea(Term, Start, Count);
+}
+void Display_ScrollDown(tTerminal *Term, int Count) {
+}
+void Display_SetCursor(tTerminal *Term, int Row, int Col) {
+}
+void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol) {
+}
+void Display_SaveCursor(tTerminal *Term) {
+}
+void Display_RestoreCursor(tTerminal *Term) {
+}
+void Display_ClearLine(tTerminal *Term, int Dir) {     // 0: All, 1: Forward, -1: Reverse
+}
+void Display_ClearLines(tTerminal *Term, int Dir) {    // 0: All, 1: Forward, -1: Reverse
+}
+void Display_ResetAttributes(tTerminal *Term) {
+}
+void Display_SetForeground(tTerminal *Term, uint32_t RGB) {
+}
+void Display_SetBackground(tTerminal *Term, uint32_t RGB) {
+}
+void Display_Flush(tTerminal *Term) {
+}
+void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled) {
+}
+void Display_SetTitle(tTerminal *Term, const char *Title) {
+       TEST_ASSERT(gpTest_VT100_SetTitle);
+       gpTest_VT100_SetTitle(Title);
+}
diff --git a/Usermode/Applications/gui_terminal_src/UTEST/test_common.c b/Usermode/Applications/gui_terminal_src/UTEST/test_common.c
new file mode 100644 (file)
index 0000000..a07dd1d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ */
+#include "test_common.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <stdarg.h>
+
+// === GLOBALS ===
+int    gbDebugEnabled = 0;
+jmp_buf        gTest_FailJump;
+
+// === CODE ===
+int main(int argc, char *argv[])
+{
+        int    nFail = 0;
+       for( int i = 0; i < giNumTests; i ++ )
+       {
+               tTEST   *test = &gaTests[i];
+               fprintf(stderr, "Test [%i] \"%s\": ", i, test->Name);
+               if( test->Setup && test->Setup() != 0 ) {
+                       fprintf(stderr, "ERROR\n");
+               }
+               else {
+                       int res = setjmp(gTest_FailJump);
+                       if( res == 0 )
+                               test->Run();
+                       
+                       if(test->Teardown)
+                               test->Teardown();
+                       if( res == 0 )
+                               fprintf(stderr, "PASS\n");
+                       else
+                               nFail ++;
+               }
+       }
+       return nFail;
+}
+
+void TestFailure(const char *ReasonFmt, ...)
+{
+       va_list args;
+       va_start(args, ReasonFmt);
+       fprintf(stderr, "FAILURE: ");
+       vfprintf(stderr, ReasonFmt, args);
+       fprintf(stderr, "\n");
+       va_end(args);
+       longjmp(gTest_FailJump, 1);
+}
+
+void _SysDebug(const char *Format, ...)
+{
+       if( gbDebugEnabled )
+       {
+               va_list args;
+               va_start(args, Format);
+               printf("_SysDebug: ");
+               vprintf(Format, args);
+               printf("\n");
+               va_end(args);
+       }
+}
diff --git a/Usermode/Applications/gui_terminal_src/UTEST/test_common.h b/Usermode/Applications/gui_terminal_src/UTEST/test_common.h
new file mode 100644 (file)
index 0000000..e6fde41
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _TEST_COMMON_H
+#define _TEST_COMMON_H
+
+typedef struct
+{
+       const char      *Name;
+        int    (*Setup)(void);
+       void    (*Run)(void);
+       void    (*Teardown)(void);
+} tTEST;
+
+extern tTEST   gaTests[];
+extern int     giNumTests;
+
+extern void    TestFailure(const char *ReasonFmt, ...) __attribute__((noreturn));
+
+#define TEST_ASSERT(expr) do{ \
+       if( !(expr) )   TestFailure("Assertion %s", #expr );\
+} while(0)
+
+#define TEST_ASSERT_R(a,r,b) do{ \
+       if( !((a) r (b)) )      TestFailure("Assertion %s(%i) %s %s(%i)", #a, (a), #r, #b, (b) );\
+} while(0)
+#define TEST_ASSERT_SR(a,r,b) do{ \
+       if( !(strcmp((a),(b)) r 0) )    TestFailure("Assertion %s(\"%s\") %s %s(\"%s\")", #a, (a), #r, #b, (b) );\
+} while(0)
+
+#endif
index 4af6403..91967ff 100644 (file)
@@ -15,6 +15,7 @@
 # include <acess/sys.h>        // _SysDebug
 # include <string.h>
 # include <assert.h>
+# include <stdlib.h>   // malloc/free
 #endif
 
 enum eExcapeMode {
@@ -49,8 +50,9 @@ const uint32_t        caVT100Colours[] = {
 };
 
  int   _locate_eos(size_t Len, const char *Buf);
- int   Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf);
  int   Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf);
+ int   Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf);
+ int   Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf);
 
 static inline int min(int a, int b)
 {
@@ -59,8 +61,7 @@ static inline int min(int a, int b)
 
 int _locate_eos(size_t Len, const char *Buf)
 {
-       size_t  ret = 0;
-       while( ret < Len )
+       for( size_t ret = 0; ret < Len; ret ++ )
        {
                if( Buf[ret] == '\007' )
                        return ret;
@@ -88,20 +89,23 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
        if( st->Mode == MODE_IGNORE ) {
                st->Mode = MODE_NORMAL;
                // Used for multi-byte EOS
+               _SysDebug("Ignore 1 '%c'", *Buf);
                return 1;
        }
-       if( st->Mode == MODE_STRING )
+       else if( st->Mode == MODE_STRING )
        {
                // We're in a string mode
                int pos = _locate_eos(Len, Buf);
                size_t bytes = (pos >= 0 ? pos : Len);
                char *tmp = realloc(st->StringCache, st->StringLen + bytes+1);
                if(!tmp)        return bytes;
+               st->StringCache = tmp;
                memcpy(tmp+st->StringLen, Buf, bytes);
                tmp[st->StringLen+bytes] = 0;
+               st->StringLen += bytes;
                
                _SysDebug("pos=%i", pos);
-               _SysDebug("'%.*s'", bytes, Buf);
+               _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf);
                // Only apply when we hit EOS at the start
                if( pos != 0 )
                        return bytes;
@@ -125,6 +129,9 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
                        st->Mode = MODE_NORMAL;
                return 1;
        }
+       else {
+               // fall through
+       }
 
        if( inc_len > 0 || *Buf == '\x1b' )
        {
@@ -210,7 +217,6 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
 
 int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
 {
-       tVT100State     *st = Display_GetTermState(Term);
         int    tmp;
        switch(Buf[1])
        {
@@ -221,10 +227,11 @@ int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
                        return 0;
                return tmp + 2;
        case ']':
-               st->Mode = MODE_STRING;
-               st->StringType = STRING_IGNORE;
-               _SysDebug("TODO: \\e] Support title changes");
-               return 2;
+               tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2);
+               assert(tmp >= 0);
+               if( tmp == 0 )
+                       return 0;
+               return tmp + 2;
        case '=':
                _SysDebug("TODO: \\e= Application Keypad");
                return 2;
@@ -471,3 +478,61 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
        }
        return j;
 }
+
+int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf)
+{
+       tVT100State     *st = Display_GetTermState(Term);
+
+        int    ofs = 0;
+       // OSC Ps ; Pt [ST/BEL]
+       if(Len < 2)     return 0;       // Need moar
+
+        int    Ps = 0;
+       while( ofs < Len && isdigit(Buf[ofs]) ) {
+               Ps = Ps * 10 + (Buf[ofs] - '0');
+               ofs ++;
+       }
+
+       if( ofs == Len )        return 0;
+       if( Buf[ofs] != ';' ) {
+               // Error
+               st->Mode = MODE_STRING;
+               st->StringType = STRING_IGNORE;
+               return ofs;
+       }
+       ofs ++;
+
+       switch(Ps)
+       {
+       case 0: // Icon Name + Window Title
+       case 1: // Icon Name
+       case 2: // Window Title
+               st->Mode = MODE_STRING;
+               st->StringType = STRING_TITLE;
+               break;
+       case 3: // Set X Property
+               _SysDebug("TODO: \\e]3; Support X properties");
+               st->Mode = MODE_STRING;
+               st->StringType = STRING_IGNORE;
+               break;
+       case 4: // Change colour number
+       case 5: // Change special colour number
+               _SysDebug("TODO: \\e]%i;c; Support X properties", Ps);
+               st->Mode = MODE_STRING;
+               st->StringType = STRING_IGNORE;
+               break;  
+       case 10:        // Change foreground to Pt
+       case 11:        // Change background to Pt
+       case 52:
+               // TODO: Can be Pt = [cps01234567]*;<str>
+               // > Clipboard data in base-64, cut/primary/select buffers 0-7
+               // > <str>='?' returns the clipbard in the same format
+       default:
+               _SysDebug("Unknown VT100 OSC \\e]%i;", Ps);
+               st->Mode = MODE_STRING;
+               st->StringType = STRING_IGNORE;
+               break;
+       }
+       return ofs;
+}
+

UCC git Repository :: git.ucc.asn.au