Explorar o código

Working CursorPos tests.

Steve Thielemann %!s(int64=2) %!d(string=hai) anos
pai
achega
862bcb2e79
Modificáronse 6 ficheiros con 614 adicións e 595 borrados
  1. 56 30
      door/door.go
  2. 550 0
      door/door_test.go
  3. 1 2
      door/input.go
  4. 6 3
      door/input_linux.go
  5. 0 560
      door/input_test.go
  6. 1 0
      door/utilities.go

+ 56 - 30
door/door.go

@@ -34,6 +34,14 @@ import (
 	"golang.org/x/term"
 )
 
+// debugging output - enable here
+const DEBUG_DOOR bool = false
+
+// For debugging input reader routines.
+const DEBUG_INPUT = false
+
+// See door_test.go for DEBUG test const
+
 const SavePos = "\x1b[s"              // Save Cursor Position
 const RestorePos = "\x1b[u"           // Restore Cursor Position
 const CRNL = "\r\n"                   // BBS Line Ending
@@ -121,6 +129,15 @@ type ReaderData struct {
 	Err error
 }
 
+/*
+type noCopy struct{}
+
+func (*noCopy) Lock()   {}
+func (*noCopy) Unlock() {}
+
+	noCopy         noCopy
+*/
+
 type Door struct {
 	Config         DropfileConfig
 	READFD         int
@@ -160,18 +177,6 @@ func (d *Door) WriterIsClosed() bool {
 	return d.WriterClosed
 }
 
-func (d *Door) AddMouse(mouse Mouse) {
-	d.mcMutex.Lock()
-	defer d.mcMutex.Unlock()
-	d.LastMouse = append(d.LastMouse, mouse)
-}
-
-func (d *Door) GetMouse() (Mouse, bool) {
-	d.mcMutex.Lock()
-	defer d.mcMutex.Unlock()
-	return ArrayDelete(&d.LastMouse, 0)
-}
-
 /*
 Enable mouse support
 9 : X10 Support
@@ -198,15 +203,49 @@ func (d *Door) DisableMouse() {
 	d.Mouse = Off
 }
 
+func (d *Door) AddMouse(mouse Mouse) {
+	d.mcMutex.Lock()
+	defer d.mcMutex.Unlock()
+	d.LastMouse = append(d.LastMouse, mouse)
+}
+
+func (d *Door) GetMouse() (Mouse, bool) {
+	d.mcMutex.Lock()
+	defer d.mcMutex.Unlock()
+	return ArrayDelete(&d.LastMouse, 0)
+}
+
 func (d *Door) GetCursorPos() (CursorPos, bool) {
 	d.mcMutex.Lock()
+	if DEBUG_DOOR {
+		log.Printf("LastCursor %p/%p %d, %d\n", d, &d.LastCursor, len(d.LastCursor), cap(d.LastCursor))
+	}
 	defer d.mcMutex.Unlock()
+	if DEBUG_DOOR {
+		log.Printf("LastCursor: %#v\n", d.LastCursor)
+	}
 	return ArrayDelete(&d.LastCursor, 0)
 }
 
+func (d *Door) AddCursorPos(cursor CursorPos) {
+	d.mcMutex.Lock()
+	if DEBUG_DOOR {
+		log.Printf("LastCursor %p/%p %d, %d\n", d, &d.LastCursor, len(d.LastCursor), cap(d.LastCursor))
+	}
+	defer d.mcMutex.Unlock()
+	d.LastCursor = append(d.LastCursor, cursor)
+	if DEBUG_DOOR {
+		log.Printf("LastCursor now %d, %d\n", len(d.LastCursor), cap(d.LastCursor))
+		log.Printf("AddCursor: %#v\n", d.LastCursor)
+	}
+}
+
 func (d *Door) ClearMouseCursor() {
 	d.mcMutex.Lock()
 	defer d.mcMutex.Unlock()
+	if DEBUG_DOOR {
+		log.Println("ClearMouseCursor")
+	}
 	d.LastMouse = make([]Mouse, 0, 2)
 	d.LastCursor = make([]CursorPos, 0, 3)
 }
@@ -422,22 +461,16 @@ func (d *Door) Init(doorname string) {
 		log.Panicf("Error creating log file %s: %v", logfilename, err)
 	}
 
+	// Set logging output to logfilename.
+
 	log.SetOutput(logf)
 	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
 	log.Printf("Loading dropfile %s\n", dropfile)
 	log.Printf("BBS %s, User %s / Handle %s / File %d\n", d.Config.BBSID, d.Config.Real_name, d.Config.Handle, d.Config.Comm_handle)
 
 	d.readerChannel = make(chan ReaderData, 16) // was 8 ?
-	/*
-		Ok, here's the issue.  This blocks the go reader when this is full.
-		It seems like it would be better to have a channel that receives
-		rune, Extended instead.
-	*/
-	d.writerChannel = make(chan string) // unbuffered
-
-	// changing this to unbound/sync hangs tests.
-	// d.closeChannel = make(chan struct{}, 2) // reader & door.Close
-
+	d.writerChannel = make(chan string)         // unbuffered
+	d.ClearMouseCursor()
 	d.setupChannels()
 
 	d.Detect()
@@ -461,20 +494,13 @@ func (d *Door) Close() {
 
 	d.DisableMouse()
 	log.Println("Closing...")
-	// d.closeChannel <- struct{}{}
 	close(d.writerChannel)
-	/*
-		if !d.WriterClosed {
-			d.writerChannel <- ""
-		}
-	*/
-
-	// CloseReader(d.Config.Comm_handle)
 
 	log.Println("wg.Wait()")
 	d.wg.Wait()
 	log.Println("Closed.")
 	if d.Config.Comm_type == 0 {
+		// Linux - restore console settings to default/original.
 		term.Restore(d.READFD, d.tio_default)
 	}
 }

+ 550 - 0
door/door_test.go

@@ -4,6 +4,8 @@ package door
 import (
 	"flag"
 	"fmt"
+	"log"
+	"net"
 	"os"
 	"testing"
 	"time"
@@ -12,6 +14,8 @@ import (
 // Should tests not delete the logfiles?
 // To make use of this, set KEEP_LOGS bool = true and:
 // go test -c; ./door.test -test.v -test.timeout 40s
+// Running just one test:
+// go test -c; ./door.test -test.run DoorCP437 -test.v
 
 const KEEP_LOGS bool = false
 const VERBOSE_TEST bool = false
@@ -237,3 +241,549 @@ func TestDetectFail(t *testing.T) {
 	// time.Sleep(time.Duration(1) * time.Second)
 	t.Logf("Done.")
 }
+
+// Pass d*Door to test.  Otherwise we get a copy of the object passed!
+// go vet (shows this error/issue)
+
+// Generic tests that will be called from CP437 and Unicode routines
+func InputTests(t *testing.T, server net.Conn, d *Door, mode string) {
+	var err error
+	var buffer string
+
+	keytest := map[string][]rune{
+		"\x1b":             []rune{0x1b},
+		"\x0d\x00":         []rune{0x0d},
+		"\x0d\x0a":         []rune{0x0d},
+		"\x0dCQ":           []rune{0x0d, 'C', 'Q'},
+		"\x0dA":            []rune{0x0d, 'A'},
+		"\x0dCAT":          []rune{0x0d, 'C', 'A', 'T'},
+		"\U0001f920\u2615": []rune{'\U0001f920', '\u2615'},
+	}
+
+	keyextest := map[string][]Extended{
+		"\x00\x50\x00\x48\x00\x4b\x00\x4d": []Extended{DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW},
+		"\x00\x47\x00\x4f\x00\x49\x00\x51": []Extended{HOME, END, PAGE_UP, PAGE_DOWN},
+		"\x00\x3b\x00\x3c\x00\x3d\x00\x3e": []Extended{F1, F2, F3, F4},
+		"\x00\x3f\x00\x40\x00\x41\x00\x42": []Extended{F5, F6, F7, F8},
+		"\x00\x43\x00\x44\x00\x52\x00\x53": []Extended{F9, F10, INSERT, DELETE},
+		"\x1b[A\x1b[B\x1b[C\x1b[D":         []Extended{UP_ARROW, DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW},
+		"\x1b[H\x1b[F\x1b[K\x1b[V\x1b[U":   []Extended{HOME, END, END, PAGE_UP, PAGE_DOWN},
+		"\x1b[5~\x1b[6~":                   []Extended{PAGE_UP, PAGE_DOWN},
+		"\x1b[@\x1b[2~\x1b[3~":             []Extended{INSERT, INSERT, DELETE},
+		"\x1bOP\x1bOQ\x1bOR\x1bOS":         []Extended{F1, F2, F3, F4},
+		"\x1b[15~\x1b[17~\x1b[18~\x1b[19~": []Extended{F5, F6, F7, F8},
+		"\x1b[20~\x1b[21~\x1b[23~\x1b[24~": []Extended{F9, F10, F11, F12},
+	}
+
+	keyWait := time.Duration(50 * time.Millisecond)
+
+	// Verify input is empty
+	for {
+		input, ex, err := d.WaitKey(keyWait)
+		if err == nil {
+			t.Errorf("Keys: %#v, %#v\n", input, ex)
+		} else {
+			break
+		}
+	}
+
+	t.Logf("Starting keytest (%s)\n", mode)
+
+	for send, get := range keytest {
+		if VERBOSE_TEST {
+			t.Logf("Sending %#v: Expect: %#v\n", send, get)
+		}
+		var buffer []byte = []byte(send)
+		_, err = server.Write(buffer)
+		if err != nil {
+			t.Error("server.Write:", err)
+		}
+		time.Sleep(time.Millisecond)
+
+		var recv []rune = make([]rune, 0)
+		var retries int = 0
+		for {
+			input, _, err := d.WaitKey(keyWait)
+			if VERBOSE_TEST {
+				t.Logf(">> %#v, %#v\n", input, err)
+			}
+
+			if err == nil {
+				recv = append(recv, input)
+				if len(recv) != len(get) {
+					continue
+				}
+				break
+			} else {
+				t.Logf("WaitKey Err: %#v\n", err)
+				if len(recv) != len(get) {
+					if retries < 5 {
+						retries++
+						t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
+						continue
+					}
+					break
+				}
+			}
+		}
+
+		if len(recv) != len(get) {
+			t.Errorf("Sent %#v, LEN expected %#v, got %#v", send, get, recv)
+		} else {
+			matches := true
+			for idx, i := range get {
+				if recv[idx] != i {
+					matches = false
+					break
+				}
+			}
+			if !matches {
+				t.Errorf("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
+			}
+		}
+	}
+
+	t.Logf("Starting keyextest (%s)\n", mode)
+
+	for send, get := range keyextest {
+		if VERBOSE_TEST {
+			t.Logf("Sending %#v: Expect: %#v\n", send, get)
+		}
+		var buffer []byte = []byte(send)
+		_, err = server.Write(buffer)
+		if err != nil {
+			t.Error("server.Write:", err)
+		}
+		time.Sleep(time.Millisecond)
+
+		var recv []Extended = make([]Extended, 0)
+		var retries int = 0
+		for {
+			_, ex, err := d.WaitKey(keyWait)
+
+			if err == nil {
+				recv = append(recv, ex)
+
+				if len(recv) != len(get) {
+					continue
+				}
+				break
+			} else {
+				t.Logf("WaitKey err: #%v\n", err)
+				if len(recv) != len(get) {
+					if retries < 5 {
+						retries++
+						t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
+						continue
+					}
+
+					break
+
+				}
+			}
+		}
+
+		if len(recv) != len(get) {
+			t.Errorf("Sent %#v, LEN expected %#v, got %#v", send, get, recv)
+		} else {
+			matches := true
+			for idx, i := range get {
+				if recv[idx] != i {
+					matches = false
+					break
+				}
+			}
+			if !matches {
+				t.Errorf("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
+			}
+		}
+	}
+
+	clear_socket(server, t)
+
+	t.Logf("Starting WaitKey timeout (%s)...\n", mode)
+	_, _, timeout := d.WaitKey(keyWait)
+	if timeout == nil {
+		t.Errorf("Expected timeout, got %d / %X", timeout, timeout)
+	} else {
+		t.Logf("Ok! Buffer should be empty!  -1 (timeout)")
+	}
+
+	t.Logf("Starting input test (%s)\n", mode)
+	// Input test
+	buffer = "1234567890\r"
+	_, err = server.Write([]byte(buffer))
+	if err != nil {
+		t.Error("server.Write:", err)
+	}
+	time.Sleep(time.Millisecond)
+
+	var input string = d.Input(5)
+
+	if input != "12345" {
+		t.Errorf("Expected Input(5) = 12345, but got %#v", input)
+	}
+
+	// read everything
+	var result string = clear_socket(server, t)
+
+	expected := "     \x08\x08\x08\x08\x0812345\x07\x07\x07\x07"
+	if result != expected {
+		t.Errorf("Buffer Input(5): Expected %#v, got %#v\n", expected, result)
+	}
+	err = server.SetReadDeadline(time.Time{})
+	if err != nil {
+		t.Error("server.SetReadDeadLine:", err)
+	}
+
+	buffer = "12345678\x08\x089\r"
+	_, err = server.Write([]byte(buffer))
+	if err != nil {
+		t.Error("server.Write:", err)
+	}
+	time.Sleep(time.Millisecond)
+
+	input = d.Input(5)
+
+	if input != "1239" {
+		t.Errorf("Expected Input(5) = 1239, but got %#v", input)
+	}
+
+	buffer = "12\x08\x08\x08987\x00\x48654321\r"
+	_, err = server.Write([]byte(buffer))
+	if err != nil {
+		t.Error("server.Write:", err)
+	}
+	time.Sleep(time.Millisecond)
+
+	input = d.Input(5)
+
+	if input != "98765" {
+		t.Errorf("Expected Input(5) = 98765, but got %#v", input)
+	}
+
+	t.Logf("Starting cursor pos test (%s)\n", mode)
+	var cpos []CursorPos = []CursorPos{{1, 1}, {50, 5}, {4, 40}, {80, 25}, {160, 55}}
+	for _, pos := range cpos {
+		if VERBOSE_TEST {
+			t.Logf("Sending CursorPos(%d,%d)\n", pos.X, pos.Y)
+		}
+		buffer = CursorReply(pos.X, pos.Y)
+		_, err := server.Write([]byte(buffer))
+		if err != nil {
+			t.Error("server.Write:", err)
+		}
+	}
+
+	log.Printf("d = %p / %p\n", &d, &d.LastCursor)
+
+	for _, pos := range cpos {
+		_, ex, err := d.WaitKey(keyWait)
+		if err != nil {
+			t.Error("WaitKey:", err)
+		}
+
+		if ex != CURSOR {
+			t.Errorf("Expected Extended = CURSOR (was %s)", ex.String())
+			continue
+		}
+
+		cp, ok := d.GetCursorPos()
+
+		if !ok {
+			t.Error("Extended sent MOUSE, but GetMouse failed.")
+			continue
+		}
+
+		if (cp.X != pos.X) || (cp.Y != pos.Y) {
+			t.Errorf("Expected (%d,%d), Got (%d,%d)", pos.X, pos.Y, cp.X, cp.Y)
+		}
+	}
+
+}
+
+func TestDoorCP437(t *testing.T) {
+	var tmpFile *os.File
+	var err error
+	// Dropfile
+	tmpFile, err = os.CreateTemp("", "test-*")
+	if err != nil {
+		panic("Cannot create temporary file")
+	}
+
+	defer os.Remove(tmpFile.Name())
+
+	// establish network socket connection to set Comm_handle
+	var server, client net.Conn
+	server, client = setupSockets()
+
+	// Ok, we have a server socket, and the client socket (that the door would talk to)
+
+	// CP437 80x25 response
+	buffer := CP437WidthHeight(80, 25)
+	_, err = server.Write([]byte(buffer))
+	if err != nil {
+		t.Error("server.Write:", err)
+	}
+	time.Sleep(time.Millisecond)
+
+	// Access Fd (File descriptor) of client for dropfile
+	var fd int = socket_to_fd(client)
+	defer close_fd(fd)
+
+	// Create door32.sys file
+	var node int = 10
+	dfc := DropfileConfig{2, fd, 1800, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 0, node}
+
+	_, err = tmpFile.WriteString(fmt.Sprintf("%d\n%d\n%d\n%s\n%d\n%s\n%s\n%d\n%d\n%d\n%d\n",
+		dfc.Comm_type, dfc.Comm_handle, dfc.Baudrate, dfc.BBSID, dfc.User_number, dfc.Real_name, dfc.Handle,
+		dfc.Security_level, dfc.Time_left, dfc.Emulation, dfc.Node))
+	if err != nil {
+		t.Error("tmpFile.WriteString:", err)
+	}
+	err = tmpFile.Close()
+	if err != nil {
+		t.Error("tmpFile.Close:", err)
+	}
+
+	d := Door{} // Deprecated: ReaderCanClose: true}
+
+	// Because we're not the only one calling door.Init(), the
+	// door global variables might be from a previous test run.
+	// Reset back to defaults.
+
+	Unicode = false
+	CP437 = false
+	Full_CP437 = false
+	Width = 0
+	Height = 0
+
+	// If I call d.Init() more then once flag complains about flag redefined.
+	// Reset flags
+	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
+	// preset commandline args so door can init
+	os.Args = []string{"door", "-d", tmpFile.Name()}
+	d.Init("input-test")
+
+	defer d.Close()
+	// clean up logfile - (filename is from Init name + node #)
+	if !KEEP_LOGS {
+		defer os.Remove(fmt.Sprintf("input-test-%d.log", node))
+	}
+
+	if VERBOSE_TEST {
+		t.Logf("Verify Unicode %dX%d\n", 80, 25)
+	}
+
+	// Ok!
+	if Unicode {
+		t.Errorf("Unicode is %t", Unicode)
+	}
+
+	if !CP437 {
+		t.Errorf("Expect CP437 true, is %t", CP437)
+	}
+
+	if Width != 80 {
+		t.Errorf("Width not 80: %d", Width)
+	}
+
+	if Height != 25 {
+		t.Errorf("Height not 25: %d", Height)
+	}
+
+	clear_socket(server, t)
+
+	InputTests(t, server, &d, "CP437")
+
+	t.Logf("server close")
+	log.Println("server close")
+
+	server.Close()
+
+	time.Sleep(time.Millisecond)
+	keyWait := time.Duration(50 * time.Millisecond)
+
+	_, _, err = d.WaitKey(keyWait)
+	if err != ErrDisconnected {
+		t.Errorf("Expected ErrDisconnected, got %#v", err)
+	}
+
+	if !d.Disconnect() {
+		t.Errorf("Disconnected flag shows: %t (should be true)", d.Disconnect())
+	}
+
+	_, _, err = d.WaitKey(time.Millisecond)
+
+	if err != ErrDisconnected {
+		t.Errorf("Expected ErrDisconnected, got %#v", err)
+	}
+
+	t.Logf("client close")
+	client.Close()
+	time.Sleep(time.Millisecond)
+
+	t.Logf("Input on closed server and client")
+	var blank string = d.Input(5)
+
+	if blank != "" {
+		t.Errorf("Input should return blank (hangup).")
+	}
+
+	_, _, err = d.WaitKey(time.Millisecond)
+
+	if err != ErrDisconnected {
+		t.Errorf("Expected ErrDisconnected, got %#v", err)
+	}
+
+	d.Write("\x00")
+	time.Sleep(time.Millisecond)
+}
+
+func TestDoorUnicode(t *testing.T) {
+	var tmpFile *os.File
+	var err error
+
+	// Dropfile
+	tmpFile, err = os.CreateTemp("", "test-*")
+	if err != nil {
+		panic("Cannot create temporary file")
+	}
+
+	defer os.Remove(tmpFile.Name())
+
+	// establish network socket connection to set Comm_handle
+	var server, client net.Conn
+	server, client = setupSockets()
+
+	// Ok, we have a server socket, and the client socket (that the door would talk to)
+
+	// unicode 190x43 response
+	buffer := UnicodeWidthHeight(190, 43)
+	_, err = server.Write([]byte(buffer))
+	if err != nil {
+		t.Error("server.Write:", err)
+	}
+	time.Sleep(time.Millisecond)
+
+	// Access Fd (File descriptor) of client for dropfile
+	var fd int = socket_to_fd(client)
+	defer close_fd(fd)
+
+	// Create door32.sys file
+	var node int = 11
+	dfc := DropfileConfig{2, fd, 1800, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 0, node}
+
+	_, err = tmpFile.WriteString(fmt.Sprintf("%d\n%d\n%d\n%s\n%d\n%s\n%s\n%d\n%d\n%d\n%d\n",
+		dfc.Comm_type, dfc.Comm_handle, dfc.Baudrate, dfc.BBSID, dfc.User_number, dfc.Real_name, dfc.Handle,
+		dfc.Security_level, dfc.Time_left, dfc.Emulation, dfc.Node))
+	if err != nil {
+		t.Error("tmpFile.WriteString:", err)
+	}
+	err = tmpFile.Close()
+	if err != nil {
+		t.Error("tmpFile.Close:", err)
+	}
+
+	d := Door{} // Deprecated: ReaderCanClose: true}
+
+	// Because we're not the only one calling door.Init(), the
+	// door global variables might be from a previous test run.
+	// Reset back to defaults.
+
+	Unicode = false
+	CP437 = false
+	Full_CP437 = false
+	Width = 0
+	Height = 0
+
+	// If I call d.Init() more then once flag complains about flag redefined.
+	// Reset flags
+	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
+	// preset commandline args so door can init
+	os.Args = []string{"door", "-d", tmpFile.Name()}
+	d.Init("input-test")
+
+	defer d.Close()
+	// clean up logfile - (filename is from Init name + node #)
+	if !KEEP_LOGS {
+		defer os.Remove(fmt.Sprintf("input-test-%d.log", node))
+	}
+
+	// Ok!
+	if !Unicode {
+		t.Errorf("Unicode not true %t", Unicode)
+	}
+
+	if Width != 190 {
+		t.Errorf("Width not 190: %d", Width)
+	}
+
+	if Height != 43 {
+		t.Errorf("Height not 43: %d", Height)
+	}
+
+	clear_socket(server, t)
+
+	keyWait := time.Duration(50 * time.Millisecond)
+	InputTests(t, server, &d, "Unicode")
+
+	buffer = "\U0001f9201234\r"
+	// double wide (2) + 4 = 6, we're inputting 5.
+	_, err = server.Write([]byte(buffer))
+	if err != nil {
+		t.Error("server.Write:", err)
+	}
+	time.Sleep(time.Millisecond)
+
+	// Unicode mode can handle unicode input.  CP437 mode can't -- really.
+	input := d.Input(5)
+	expect := "\U0001f920123"
+	if input != expect {
+		t.Errorf("Expected Input(5) = %#v, but got %#v", expect, input)
+	}
+
+	clear_socket(server, t)
+
+	t.Logf("server close")
+	log.Println("server close")
+
+	server.Close()
+
+	time.Sleep(time.Millisecond)
+
+	_, _, err = d.WaitKey(keyWait)
+	if err != ErrDisconnected {
+		t.Errorf("Expected ErrDisconnected, got %#v", err)
+	}
+
+	if !d.Disconnect() {
+		t.Errorf("Disconnected flag shows: %t (should be true)", d.Disconnect())
+	}
+
+	_, _, err = d.WaitKey(time.Millisecond)
+
+	if err != ErrDisconnected {
+		t.Errorf("Expected ErrDisconnected, got %#v", err)
+	}
+
+	t.Logf("client close")
+	client.Close()
+	time.Sleep(time.Millisecond)
+
+	t.Logf("Input on closed server and client")
+	var blank string = d.Input(5)
+
+	if blank != "" {
+		t.Errorf("Input should return blank (hangup).")
+	}
+
+	_, _, err = d.WaitKey(time.Millisecond)
+
+	if err != ErrDisconnected {
+		t.Errorf("Expected ErrDisconnected, got %#v", err)
+	}
+
+	d.Write("\x00")
+	time.Sleep(time.Millisecond)
+}

+ 1 - 2
door/input.go

@@ -9,8 +9,7 @@ import (
 	"unicode"
 )
 
-// For debugging input reader routines.
-const DEBUG_INPUT = false
+// See door.go for DEBUG_INPUT const
 
 var ErrInactivity error = fmt.Errorf("Inactivity")
 var ErrTimeout error = fmt.Errorf("Timeout")

+ 6 - 3
door/input_linux.go

@@ -341,9 +341,12 @@ func process(d *Door, newRune bool) {
 					if len(pos) == 2 {
 						cursor.X = pos[1]
 						cursor.Y = pos[0]
-						d.mcMutex.Lock()
-						d.LastCursor = append(d.LastCursor, cursor)
-						d.mcMutex.Unlock()
+						d.AddCursorPos(cursor)
+						/*
+							d.mcMutex.Lock()
+							d.LastCursor = append(d.LastCursor, cursor)
+							d.mcMutex.Unlock()
+						*/
 						log.Println("Cursor Pos:", cursor)
 						d.readerChannel <- ReaderData{0, CURSOR, nil}
 						continue

+ 0 - 560
door/input_test.go

@@ -1,569 +1,9 @@
 package door
 
 import (
-	"flag"
-	"fmt"
-
-	"log"
-	"net"
-	"os"
 	"testing"
-	"time"
 )
 
-func TestDoorCP437(t *testing.T) {
-	var tmpFile *os.File
-	var err error
-	tmpFile, err = os.CreateTemp("", "test-*")
-	if err != nil {
-		panic("Cannot create temporary file")
-	}
-
-	// Remember to clean up the file afterwards
-	if !KEEP_LOGS {
-		defer os.Remove(tmpFile.Name())
-	} else {
-		t.Logf("See: %s\n", tmpFile.Name())
-	}
-
-	// establish network socket connection to set Comm_handle
-	var server, client net.Conn
-	server, client = setupSockets()
-
-	// Ok, we have a server socket, and the client socket (that the door would talk to)
-
-	// CP437 80x25 response
-	buffer := CP437WidthHeight(80, 25)
-	_, err = server.Write([]byte(buffer))
-	if err != nil {
-		t.Error("server.Write:", err)
-	}
-	time.Sleep(time.Millisecond)
-
-	// Access Fd (File descriptor) of client for dropfile
-	var fd int = socket_to_fd(client)
-	defer close_fd(fd)
-
-	// Create door32.sys file
-	dfc := DropfileConfig{2, fd, 1800, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 0, 12}
-
-	_, err = tmpFile.WriteString(fmt.Sprintf("%d\n%d\n%d\n%s\n%d\n%s\n%s\n%d\n%d\n%d\n%d\n",
-		dfc.Comm_type, dfc.Comm_handle, dfc.Baudrate, dfc.BBSID, dfc.User_number, dfc.Real_name, dfc.Handle,
-		dfc.Security_level, dfc.Time_left, dfc.Emulation, dfc.Node))
-	if err != nil {
-		t.Error("tmpFile.WriteString:", err)
-	}
-	err = tmpFile.Close()
-	if err != nil {
-		t.Error("tmpFile.Close:", err)
-	}
-
-	d := Door{} // Deprecated: ReaderCanClose: true}
-
-	// Because we're not the only one calling door.Init(), the
-	// door global variables might be from a previous test run.
-
-	Unicode = false
-	CP437 = false
-	Full_CP437 = false
-	Width = 0
-	Height = 0
-
-	// If I call d.Init() more then once flag complains about flag redefined.
-	// Reset flags
-	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
-	// preset commandline args so door can init
-	os.Args = []string{"door", "-d", tmpFile.Name()}
-	d.Init("input-test")
-
-	defer d.Close()
-	// clean up logfile - (filename is from Init name + node #)
-	if !KEEP_LOGS {
-		defer os.Remove("input-test-12.log")
-	}
-
-	// Ok!
-	if Unicode {
-		t.Errorf("Unicode is %t", Unicode)
-	}
-
-	if !CP437 {
-		t.Errorf("Expect CP437 true, is %t", CP437)
-	}
-
-	if Width != 80 {
-		t.Errorf("Width not 80: %d", Width)
-	}
-
-	if Height != 25 {
-		t.Errorf("Height not 25: %d", Height)
-	}
-
-	clear_socket(server, t)
-	// This should all be received by above (I think)...
-
-	t.Logf("server close")
-	log.Println("server close")
-
-	server.Close()
-
-	time.Sleep(time.Millisecond)
-	keyWait := time.Duration(50 * time.Millisecond)
-
-	_, _, err = d.WaitKey(keyWait)
-	if err != ErrDisconnected {
-		t.Errorf("Expected ErrDisconnected, got %#v", err)
-	}
-
-	if !d.Disconnect() {
-		t.Errorf("Disconnected flag shows: %t (should be true)", d.Disconnect())
-	}
-
-	/*
-		if !d.HasKey() {
-			t.Error("HasKey should return true (disconnected).")
-		}
-	*/
-
-	_, _, err = d.WaitKey(time.Millisecond) // GetKey()
-
-	if err != ErrDisconnected {
-		t.Errorf("Expected ErrDisconnected, got %#v", err)
-	}
-
-	t.Logf("client close")
-	client.Close()
-	time.Sleep(time.Millisecond)
-
-	t.Logf("Input on closed server and client")
-	var blank string = d.Input(5)
-
-	if blank != "" {
-		t.Errorf("Input should return blank (hangup).")
-	}
-
-	_, _, err = d.WaitKey(time.Millisecond) // getch()
-
-	if err != ErrDisconnected {
-		t.Errorf("Expected ErrDisconnected, got %#v", err)
-	}
-
-	d.Write("\x00")
-	time.Sleep(time.Millisecond)
-}
-
-func TestDoorInputConnection(t *testing.T) {
-	var tmpFile *os.File
-	var err error
-	tmpFile, err = os.CreateTemp("", "test-*")
-	if err != nil {
-		panic("Cannot create temporary file")
-	}
-
-	// Remember to clean up the file afterwards
-	if !KEEP_LOGS {
-		defer os.Remove(tmpFile.Name())
-	} else {
-		t.Logf("See: %s\n", tmpFile.Name())
-	}
-
-	// establish network socket connection to set Comm_handle
-	var server, client net.Conn
-	server, client = setupSockets()
-
-	// Ok, we have a server socket, and the client socket (that the door would talk to)
-
-	// unicode 190x43 response
-	buffer := UnicodeWidthHeight(190, 43)
-	_, err = server.Write([]byte(buffer))
-	if err != nil {
-		t.Error("server.Write:", err)
-	}
-	time.Sleep(time.Millisecond)
-
-	// Access Fd (File descriptor) of client for dropfile
-	var fd int = socket_to_fd(client)
-	defer close_fd(fd)
-
-	// Create door32.sys file
-	dfc := DropfileConfig{2, fd, 1800, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 0, 12}
-
-	_, err = tmpFile.WriteString(fmt.Sprintf("%d\n%d\n%d\n%s\n%d\n%s\n%s\n%d\n%d\n%d\n%d\n",
-		dfc.Comm_type, dfc.Comm_handle, dfc.Baudrate, dfc.BBSID, dfc.User_number, dfc.Real_name, dfc.Handle,
-		dfc.Security_level, dfc.Time_left, dfc.Emulation, dfc.Node))
-	if err != nil {
-		t.Error("tmpFile.WriteString:", err)
-	}
-	err = tmpFile.Close()
-	if err != nil {
-		t.Error("tmpFile.Close:", err)
-	}
-
-	d := Door{} // Deprecated: ReaderCanClose: true}
-
-	// Because we're not the only one calling door.Init(), the
-	// door global variables might be from a previous test run.
-
-	Unicode = false
-	CP437 = false
-	Full_CP437 = false
-	Width = 0
-	Height = 0
-
-	// If I call d.Init() more then once flag complains about flag redefined.
-	// Reset flags
-	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
-	// preset commandline args so door can init
-	os.Args = []string{"door", "-d", tmpFile.Name()}
-	d.Init("input-test")
-
-	defer d.Close()
-	// clean up logfile - (filename is from Init name + node #)
-	if !KEEP_LOGS {
-		defer os.Remove("input-test-12.log")
-	}
-
-	// Ok!
-	if !Unicode {
-		t.Errorf("Unicode not true %t", Unicode)
-	}
-
-	if Width != 190 {
-		t.Errorf("Width not 190: %d", Width)
-	}
-
-	if Height != 43 {
-		t.Errorf("Height not 43: %d", Height)
-	}
-
-	clear_socket(server, t)
-	// This should all be received by above (I think)...
-
-	keytest := map[string][]rune{
-		"\x1b":     []rune{0x1b},
-		"\x0d\x00": []rune{0x0d},
-		"\x0d\x0a": []rune{0x0d},
-		"\x0dCQ":   []rune{0x0d, 'C', 'Q'},
-		"\x0dA":    []rune{0x0d, 'A'},
-		"\x0dCAT":  []rune{0x0d, 'C', 'A', 'T'},
-	}
-
-	keyextest := map[string][]Extended{
-		"\x00\x50\x00\x48\x00\x4b\x00\x4d": []Extended{DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW},
-		"\x00\x47\x00\x4f\x00\x49\x00\x51": []Extended{HOME, END, PAGE_UP, PAGE_DOWN},
-		"\x00\x3b\x00\x3c\x00\x3d\x00\x3e": []Extended{F1, F2, F3, F4},
-		"\x00\x3f\x00\x40\x00\x41\x00\x42": []Extended{F5, F6, F7, F8},
-		"\x00\x43\x00\x44\x00\x52\x00\x53": []Extended{F9, F10, INSERT, DELETE},
-		"\x1b[A\x1b[B\x1b[C\x1b[D":         []Extended{UP_ARROW, DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW},
-		"\x1b[H\x1b[F\x1b[K\x1b[V\x1b[U":   []Extended{HOME, END, END, PAGE_UP, PAGE_DOWN},
-		"\x1b[5~\x1b[6~":                   []Extended{PAGE_UP, PAGE_DOWN},
-		"\x1b[@\x1b[2~\x1b[3~":             []Extended{INSERT, INSERT, DELETE},
-		"\x1bOP\x1bOQ\x1bOR\x1bOS":         []Extended{F1, F2, F3, F4},
-		"\x1b[15~\x1b[17~\x1b[18~\x1b[19~": []Extended{F5, F6, F7, F8},
-		"\x1b[20~\x1b[21~\x1b[23~\x1b[24~": []Extended{F9, F10, F11, F12},
-	}
-
-	keyWait := time.Duration(50 * time.Millisecond)
-
-	// Verify input is empty
-	for {
-		input, ex, err := d.WaitKey(keyWait)
-		if err == nil {
-			t.Errorf("Keys: %#v, %#v\n", input, ex)
-		} else {
-			break
-		}
-	}
-
-	t.Logf("Starting keytest\n")
-
-	// 5 * time.Second) //50 * time.Millisecond)
-
-	for send, get := range keytest {
-		if VERBOSE_TEST {
-			t.Logf("Sending %#v: Expect: %#v\n", send, get)
-		}
-		var buffer []byte = []byte(send)
-		_, err = server.Write(buffer)
-		if err != nil {
-			t.Error("server.Write:", err)
-		}
-		time.Sleep(time.Millisecond)
-
-		var recv []rune = make([]rune, 0)
-		var retries int = 0
-		for {
-			// input := d.WaitKey(0, 50)
-			// running go test -count > 1 sometimes fails --
-			// not getting all of the data we Write above.
-			// data shows up on next read.
-			// input := d.WaitKey(0, 100) // because we are retrying, reduce the wait time
-			input, _, err := d.WaitKey(keyWait)
-			if VERBOSE_TEST {
-				t.Logf(">> %#v, %#v\n", input, err)
-			}
-
-			if err == nil {
-				recv = append(recv, input)
-				// } else {
-				// sometimes, running test using local loopback takes awhile for the characters
-				// to arrive.
-				if len(recv) != len(get) {
-					continue
-					/*
-						if retries < 5 {
-							retries++
-							t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
-							continue
-						}
-					*/
-				}
-				break
-			} else {
-				t.Logf("WaitKey Err: %#v\n", err)
-				if len(recv) != len(get) {
-					if retries < 5 {
-						retries++
-						t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
-						continue
-					}
-					break
-				}
-			}
-		}
-
-		if len(recv) != len(get) {
-			t.Errorf("Sent %#v, LEN expected %#v, got %#v", send, get, recv)
-		} else {
-			matches := true
-			for idx, i := range get {
-				if recv[idx] != i {
-					matches = false
-					break
-				}
-			}
-			if !matches {
-				t.Errorf("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
-			}
-		}
-	}
-
-	t.Logf("Starting keyextest\n")
-
-	for send, get := range keyextest {
-		var buffer []byte = []byte(send)
-		_, err = server.Write(buffer)
-		if err != nil {
-			t.Error("server.Write:", err)
-		}
-		time.Sleep(time.Millisecond)
-
-		var recv []Extended = make([]Extended, 0)
-		var retries int = 0
-		for {
-			// input := d.WaitKey(0, 50)
-			// running go test -count > 1 sometimes fails --
-			// not getting all of the data we Write above.
-			// data shows up on next read.
-			// input := d.WaitKey(0, 100) // because we are retrying, reduce the wait time
-			_, ex, err := d.WaitKey(keyWait)
-
-			if err == nil {
-				recv = append(recv, ex)
-
-				// sometimes, running test using local loopback takes awhile for the characters
-				// to arrive.
-				if len(recv) != len(get) {
-					continue
-					/*
-						if retries < 5 {
-							retries++
-							t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
-							continue
-						}
-					*/
-				}
-				break
-			} else {
-				t.Logf("WaitKey err: #%v\n", err)
-				if len(recv) != len(get) {
-					if retries < 5 {
-						retries++
-						t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
-						continue
-					}
-
-					break
-
-				}
-			}
-		}
-
-		if len(recv) != len(get) {
-			t.Errorf("Sent %#v, LEN expected %#v, got %#v", send, get, recv)
-		} else {
-			matches := true
-			for idx, i := range get {
-				if recv[idx] != i {
-					matches = false
-					break
-				}
-			}
-			if !matches {
-				t.Errorf("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
-			}
-		}
-	}
-
-	clear_socket(server, t)
-
-	/*
-		buffer = make([]byte, 128)
-		err = server.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
-		if err != nil {
-			t.Error("server.SetReadDeadLine:", err)
-		}
-		r, err := server.Read(buffer)
-		// input_test.go:131: server.Read: &net.OpError{Op:"read", Net:"tcp", Source:(*net.TCPAddr)(0xc000012f00), Addr:(*net.TCPAddr)(0xc000012f30), Err:(*poll.DeadlineExceededError)(0x6c39c0)}
-		if !strings.Contains(err.Error(), "i/o timeout") {
-			t.Errorf("Expected poll.DeadlineExceededError: %s / %#v", err.Error(), err)
-		}
-		if r != 0 {
-			t.Errorf("Buffer After KeyTest: %#v\n", buffer[:r])
-		}
-		err = server.SetReadDeadline(time.Time{})
-		if err != nil {
-			t.Error("server.SetReadDeadLine:", err)
-		}
-	*/
-
-	t.Logf("Starting WaitKey timeout...\n")
-	_, _, timeout := d.WaitKey(keyWait)
-	if timeout == nil {
-		t.Errorf("Expected timeout, got %d / %X", timeout, timeout)
-	} else {
-		t.Logf("Ok! Buffer should be empty!  -1 (timeout)")
-	}
-
-	t.Logf("Starting input test\n")
-	// Input test
-	buffer = "1234567890\r"
-	_, err = server.Write([]byte(buffer))
-	if err != nil {
-		t.Error("server.Write:", err)
-	}
-	time.Sleep(time.Millisecond)
-
-	var input string = d.Input(5)
-
-	if input != "12345" {
-		t.Errorf("Expected Input(5) = 12345, but got %#v", input)
-	}
-
-	// What's a good way to clear out the receive buffer here??
-
-	// I'm not sure what they extra characters are in the buffer here.
-	var result string = clear_socket(server, t)
-
-	/*
-		buffer = make([]byte, 128)
-		err = server.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
-		if err != nil {
-			t.Error("server.SetReadDeadLine:", err)
-		}
-		var r int
-
-		r, err = server.Read(buffer)
-		if err != nil {
-			t.Errorf("server.Read: %#v", err)
-		}
-		var result string = string(buffer[:r])
-	*/
-
-	expected := "     \x08\x08\x08\x08\x0812345\x07\x07\x07\x07"
-	if result != expected {
-		t.Errorf("Buffer Input(5): Expected %#v, got %#v\n", expected, result)
-	}
-	err = server.SetReadDeadline(time.Time{})
-	if err != nil {
-		t.Error("server.SetReadDeadLine:", err)
-	}
-
-	buffer = "12345678\x08\x089\r"
-	_, err = server.Write([]byte(buffer))
-	if err != nil {
-		t.Error("server.Write:", err)
-	}
-	time.Sleep(time.Millisecond)
-
-	input = d.Input(5)
-
-	if input != "1239" {
-		t.Errorf("Expected Input(5) = 1239, but got %#v", input)
-	}
-
-	buffer = "12\x08\x08\x08987\x00\x48654321\r"
-	_, err = server.Write([]byte(buffer))
-	if err != nil {
-		t.Error("server.Write:", err)
-	}
-	time.Sleep(time.Millisecond)
-
-	input = d.Input(5)
-
-	if input != "98765" {
-		t.Errorf("Expected Input(5) = 98765, but got %#v", input)
-	}
-
-	t.Logf("server close")
-	log.Println("server close")
-
-	server.Close()
-
-	time.Sleep(time.Millisecond)
-
-	_, _, err = d.WaitKey(keyWait)
-	if err != ErrDisconnected {
-		t.Errorf("Expected ErrDisconnected, got %#v", err)
-	}
-
-	if !d.Disconnect() {
-		t.Errorf("Disconnected flag shows: %t (should be true)", d.Disconnect())
-	}
-
-	/*
-		if !d.HasKey() {
-			t.Error("HasKey should return true (disconnected).")
-		}
-	*/
-
-	_, _, err = d.WaitKey(time.Millisecond) // GetKey()
-
-	if err != ErrDisconnected {
-		t.Errorf("Expected ErrDisconnected, got %#v", err)
-	}
-
-	t.Logf("client close")
-	client.Close()
-	time.Sleep(time.Millisecond)
-
-	t.Logf("Input on closed server and client")
-	var blank string = d.Input(5)
-
-	if blank != "" {
-		t.Errorf("Input should return blank (hangup).")
-	}
-
-	_, _, err = d.WaitKey(time.Millisecond) // getch()
-
-	if err != ErrDisconnected {
-		t.Errorf("Expected ErrDisconnected, got %#v", err)
-	}
-
-	d.Write("\x00")
-	time.Sleep(time.Millisecond)
-}
-
 func TestDisplayInput(t *testing.T) {
 	verify := map[int]string{1: " \x08",
 		2: "  \x08\x08",

+ 1 - 0
door/utilities.go

@@ -20,6 +20,7 @@ func UnicodeWidth(r rune) int {
 
 // Delete an item from an array, returning item and true on success.
 func ArrayDelete[T any](stack *[]T, pos int) (T, bool) {
+	// log.Printf("Array %d, %d\n", len(*stack), cap(*stack))
 	var result T
 	/*
 	   https://stackoverflow.com/questions/33834742/remove-and-adding-elements-to-array-in-go-lang