Skip to content

Commit

Permalink
Added test-coverage for the console output functions (#123)
Browse files Browse the repository at this point in the history
* Added test-coverage for the console output functions

We've not added complete tests for the ADM-3A driver, but we've
added enough to prove it works - by allowing output to be redirected
we can test it does the right kinda thing.

* Updated to use the correct type for our console handle

* Added trivial test

* Added version test
  • Loading branch information
skx authored Jun 19, 2024
1 parent 80f35c3 commit d3dccf2
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 36 deletions.
74 changes: 44 additions & 30 deletions consoleout/console_adm3a.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package consoleout

import "fmt"
import (
"fmt"
"io"
"os"
)

// Adm3AOutputDriver holds our state.
type Adm3AOutputDriver struct {
Expand All @@ -13,6 +17,9 @@ type Adm3AOutputDriver struct {

// y stores the cursor Y
y uint8

// writer is where we send our output
writer io.Writer
}

// GetName returns the name of this driver.
Expand All @@ -31,40 +38,40 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) {
case 0:
switch c {
case 0x07: /* BEL: flash screen */
fmt.Printf("\033[?5h\033[?5l")
fmt.Fprintf(a3a.writer, "\033[?5h\033[?5l")
case 0x7F: /* DEL: echo BS, space, BS */
fmt.Printf("\b \b")
fmt.Fprintf(a3a.writer, "\b \b")
case 0x1A: /* adm3a clear screen */
fmt.Printf("\033[H\033[2J")
fmt.Fprintf(a3a.writer, "\033[H\033[2J")
case 0x0C: /* vt52 clear screen */
fmt.Printf("\033[H\033[2J")
fmt.Fprintf(a3a.writer, "\033[H\033[2J")
case 0x1E: /* adm3a cursor home */
fmt.Printf("\033[H")
fmt.Fprintf(a3a.writer, "\033[H")
case 0x1B:
a3a.status = 1 /* esc-prefix */
case 1:
a3a.status = 2 /* cursor motion prefix */
case 2: /* insert line */
fmt.Printf("\033[L")
fmt.Fprintf(a3a.writer, "\033[L")
case 3: /* delete line */
fmt.Printf("\033[M")
fmt.Fprintf(a3a.writer, "\033[M")
case 0x18, 5: /* clear to eol */
fmt.Printf("\033[K")
fmt.Fprintf(a3a.writer, "\033[K")
case 0x12, 0x13:
// nop
default:
fmt.Printf("%c", c)
fmt.Fprintf(a3a.writer, "%c", c)
}
case 1: /* we had an esc-prefix */
switch c {
case 0x1B:
fmt.Printf("%c", c)
fmt.Fprintf(a3a.writer, "%c", c)
case '=', 'Y':
a3a.status = 2
case 'E': /* insert line */
fmt.Printf("\033[L")
fmt.Fprintf(a3a.writer, "\033[L")
case 'R': /* delete line */
fmt.Printf("\033[M")
fmt.Fprintf(a3a.writer, "\033[M")
case 'B': /* enable attribute */
a3a.status = 4
case 'C': /* disable attribute */
Expand All @@ -75,58 +82,58 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) {
a3a.status = 8
default: /* some true ANSI sequence? */
a3a.status = 0
fmt.Printf("%c%c", 0x1B, c)
fmt.Fprintf(a3a.writer, "%c%c", 0x1B, c)
}
case 2:
a3a.y = c - ' ' + 1
a3a.status = 3
case 3:
a3a.x = c - ' ' + 1
a3a.status = 0
fmt.Printf("\033[%d;%dH", a3a.y, a3a.x)
fmt.Fprintf(a3a.writer, "\033[%d;%dH", a3a.y, a3a.x)
case 4: /* <ESC>+B prefix */
a3a.status = 0
switch c {
case '0': /* start reverse video */
fmt.Printf("\033[7m")
fmt.Fprintf(a3a.writer, "\033[7m")
case '1': /* start half intensity */
fmt.Printf("\033[1m")
fmt.Fprintf(a3a.writer, "\033[1m")
case '2': /* start blinking */
fmt.Printf("\033[5m")
fmt.Fprintf(a3a.writer, "\033[5m")
case '3': /* start underlining */
fmt.Printf("\033[4m")
fmt.Fprintf(a3a.writer, "\033[4m")
case '4': /* cursor on */
fmt.Printf("\033[?25h")
fmt.Fprintf(a3a.writer, "\033[?25h")
case '5': /* video mode on */
// nop
case '6': /* remember cursor position */
fmt.Printf("\033[s")
fmt.Fprintf(a3a.writer, "\033[s")
case '7': /* preserve status line */
// nop
default:
fmt.Printf("%cB%c", 0x1B, c)
fmt.Fprintf(a3a.writer, "%cB%c", 0x1B, c)
}
case 5: /* <ESC>+C prefix */
a3a.status = 0
switch c {
case '0': /* stop reverse video */
fmt.Printf("\033[27m")
fmt.Fprintf(a3a.writer, "\033[27m")
case '1': /* stop half intensity */
fmt.Printf("\033[m")
fmt.Fprintf(a3a.writer, "\033[m")
case '2': /* stop blinking */
fmt.Printf("\033[25m")
fmt.Fprintf(a3a.writer, "\033[25m")
case '3': /* stop underlining */
fmt.Printf("\033[24m")
fmt.Fprintf(a3a.writer, "\033[24m")
case '4': /* cursor off */
fmt.Printf("\033[?25l")
fmt.Fprintf(a3a.writer, "\033[?25l")
case '6': /* restore cursor position */
fmt.Printf("\033[u")
fmt.Fprintf(a3a.writer, "\033[u")
case '5': /* video mode off */
// nop
case '7': /* don't preserve status line */
// nop
default:
fmt.Printf("%cC%c", 0x1B, c)
fmt.Fprintf(a3a.writer, "%cC%c", 0x1B, c)
}
/* set/clear line/point */
case 6:
Expand All @@ -141,9 +148,16 @@ func (a3a *Adm3AOutputDriver) PutCharacter(c uint8) {

}

// SetWriter will update the writer.
func (a3a *Adm3AOutputDriver) SetWriter(w io.Writer) {
a3a.writer = w
}

// init registers our driver, by name.
func init() {
Register("adm-3a", func() ConsoleDriver {
return &Adm3AOutputDriver{}
return &Adm3AOutputDriver{
writer: os.Stdout,
}
})
}
19 changes: 16 additions & 3 deletions consoleout/console_ansi.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package consoleout

import "fmt"
import (
"fmt"
"io"
"os"
)

// AnsiOutputDriver holds our state.
type AnsiOutputDriver struct {
// writer is where we send our output
writer io.Writer
}

// GetName returns the name of this driver.
Expand All @@ -17,12 +23,19 @@ func (ad *AnsiOutputDriver) GetName() string {
//
// This is part of the OutputDriver interface.
func (ad *AnsiOutputDriver) PutCharacter(c uint8) {
fmt.Printf("%c", c)
fmt.Fprintf(ad.writer, "%c", c)
}

// SetWriter will update the writer.
func (ad *AnsiOutputDriver) SetWriter(w io.Writer) {
ad.writer = w
}

// init registers our driver, by name.
func init() {
Register("ansi", func() ConsoleDriver {
return &AnsiOutputDriver{}
return &AnsiOutputDriver{
writer: os.Stdout,
}
})
}
43 changes: 43 additions & 0 deletions consoleout/console_null.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package consoleout

import (
"io"
"os"
)

// NullOutputDriver holds our state.
type NullOutputDriver struct {

// writer is where we send our output
writer io.Writer
}

// GetName returns the name of this driver.
//
// This is part of the OutputDriver interface.
func (no *NullOutputDriver) GetName() string {
return "null"
}

// PutCharacter writes the specified character to the console,
// as this is a null-driver nothing happens and instead the output
// is discarded.
//
// This is part of the OutputDriver interface.
func (n *NullOutputDriver) PutCharacter(c uint8) {
// NOTHING HAppens
}

// SetWriter will update the writer.
func (n *NullOutputDriver) SetWriter(w io.Writer) {
n.writer = w
}

// init registers our driver, by name.
func init() {
Register("null", func() ConsoleDriver {
return &NullOutputDriver{
writer: os.Stdout,
}
})
}
59 changes: 58 additions & 1 deletion consoleout/console_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package consoleout

import "testing"
import (
"bytes"
"testing"
)

// TestName ensures we can lookup a driver by name
func TestName(t *testing.T) {
Expand Down Expand Up @@ -52,3 +55,57 @@ func TestChangeDriver(t *testing.T) {
t.Fatalf("driver changed unexpectedly")
}
}

// TestOutput ensures that our two "real" drivers output, as expected
func TestOutput(t *testing.T) {

// Drivers that should produce output
valid := []string{"ansi", "adm-3a"}

for _, nm := range valid {

d, e := New(nm)
if e != nil {
t.Fatalf("failed to lookup driver by name %s:%s", nm, e)
}

// ensure we redirect the output
tmp := &bytes.Buffer{}

d.driver.SetWriter(tmp)

for _, c := range "Steve Kemp" {
d.PutCharacter(byte(c))
}

// Test we got the output we expected
if tmp.String() != "Steve Kemp" {
t.Fatalf("output driver %s produced '%s'", d.GetName(), tmp.String())
}
}

}

// TestNull ensures nothing is written by the null output driver
func TestNull(t *testing.T) {

// Start with a known-good driver
null, err := New("null")
if err != nil {
t.Fatalf("failed to load starting driver %s", err)
}
if null.GetName() != "null" {
t.Fatalf("null driver has the wrong name")
}

// ensure we redirect the output
tmp := &bytes.Buffer{}

null.driver.SetWriter(tmp)

null.PutCharacter('s')

if tmp.String() != "" {
t.Fatalf("got output, expected none: '%s'", tmp.String())
}
}
8 changes: 7 additions & 1 deletion consoleout/consoleout.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// given just a name.
package consoleout

import "fmt"
import (
"fmt"
"io"
)

// ConsoleDriver is the interface that must be implemented by anything
// that wishes to be used as a console driver.
Expand All @@ -19,6 +22,9 @@ type ConsoleDriver interface {

// GetName will return the name of the driver.
GetName() string

// SetWriter will update the writer
SetWriter(io.Writer)
}

// This is a map of known-drivers
Expand Down
2 changes: 1 addition & 1 deletion cpm/cpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ type CPM struct {
input *consolein.ConsoleIn

// output is used for writing characters to the conolse
output consoleout.ConsoleDriver
output *consoleout.ConsoleOut

// dma contains the address of the DMA area in RAM.
//
Expand Down
24 changes: 24 additions & 0 deletions static/static_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package static

import (
"strings"
"testing"
)

// TestStatic just ensures we have some files.
func TestStatic(t *testing.T) {

// Read the subdirectory
files, err := Content.ReadDir("A")
if err != nil {
t.Fatalf("error reading contents")
}

// Ensure each file is a .COM files
for _, entry := range files {
name := entry.Name()
if !strings.HasSuffix(name, ".COM") {
t.Fatalf("file '%s' is not a .COM file", name)
}
}
}
17 changes: 17 additions & 0 deletions version/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package version

import (
"strings"
"testing"
)

// TestVersion is a nop-test that performs coverage of our version package.
func TestVersion(t *testing.T) {
x := GetVersionString()
y := GetVersionBanner()

// Banner should have our version
if !strings.Contains(y, x) {
t.Fatalf("banner doesn't contain our version")
}
}

0 comments on commit d3dccf2

Please sign in to comment.