Snapshot testing for screen rendering

This commit is contained in:
Ivan Izaguirre 2020-10-17 19:34:06 +02:00
parent 1a4a1507a5
commit 7329d4f042
72 changed files with 278 additions and 9 deletions

View File

@ -45,6 +45,16 @@ func (k *keyboard) putKeyAction(keyEvent *fyne.KeyEvent, press bool) {
}
}
if press && ctrl {
// F keys with ctrl do not generate events in putKey()
switch keyEvent.Name {
case fyne.KeyF1:
k.s.a.SendCommand(izapple2.CommandReset)
case fyne.KeyF12:
screen.AddScenario(k.s.a, "../screen/test_resources/")
}
}
switch keyEvent.Name {
case desktop.KeyControlLeft:
k.controlLeft = press
@ -62,7 +72,10 @@ func (k *keyboard) putKey(keyEvent *fyne.KeyEvent) {
20 PRINT A, A - 128
30 GOTO 10
*/
ctrl := k.controlLeft || k.controlRight
// Keys with control are not generating events in putKey()
//ctrl := k.controlLeft || k.controlRight
result := uint8(0)
switch keyEvent.Name {
case fyne.KeyEscape:
@ -94,15 +107,11 @@ func (k *keyboard) putKey(keyEvent *fyne.KeyEvent) {
// Control of the emulator
case fyne.KeyF1:
if ctrl {
/*if ctrl {
k.s.a.SendCommand(izapple2.CommandReset)
}
}*/
case fyne.KeyF5:
if ctrl {
k.s.a.SendCommand(izapple2.CommandShowSpeed)
} else {
k.s.a.SendCommand(izapple2.CommandToggleSpeed)
}
k.s.a.SendCommand(izapple2.CommandShowSpeed)
case fyne.KeyF6:
if k.s.screenMode != screen.ScreenModeGreen {
k.s.screenMode = screen.ScreenModeGreen

65
screen/screen_test.go Normal file
View File

@ -0,0 +1,65 @@
package screen
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
)
func TestSnapshots(t *testing.T) {
// Verifies all the scenarios on the ./test_resources folder
files, err := ioutil.ReadDir("./test_resources/")
if err != nil {
t.Fatal(err)
}
for _, fileInfo := range files {
if !fileInfo.IsDir() &&
strings.HasSuffix(fileInfo.Name(), ".json") {
testScenario(t, "./test_resources/"+fileInfo.Name())
}
}
}
func testScenario(t *testing.T, fileName string) {
ts, err := loadTestScenario(fileName)
if err != nil {
t.Fatal(err)
}
ts.generateSnapshots(fileName, true)
for _, screenMode := range ts.ScreenModes {
referenceName := buildImageName(fileName, screenMode, false)
actualName := buildImageName(fileName, screenMode, true)
reference, err := ioutil.ReadFile(referenceName)
if err != nil {
t.Fatal(err)
}
actual, err := ioutil.ReadFile(actualName)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(reference, actual) != 0 {
t.Errorf("Files %s and %s should be equal", referenceName, actualName)
replaceIfNeeded(referenceName, actualName)
} else {
os.Remove(actualName)
}
}
}
func replaceIfNeeded(referenceName string, actualName string) {
// If the "update" argument is passed to test. The new images replace the old.
// Run the tests with: "go test . -args update"
for _, arg := range os.Args {
if arg == "update" {
os.Remove(referenceName)
os.Rename(actualName, referenceName)
}
}
}

View File

@ -65,7 +65,7 @@ func VideoModeName(vs VideoSource) string {
case VideoMono560:
name = "Mono560"
case VideoRGBMix:
name = "RGMMIX"
name = "RGBMIX"
case VideoRGB160:
name = "RGB160"
case VideoSHR:

178
screen/testScenarios.go Normal file
View File

@ -0,0 +1,178 @@
package screen
import (
"encoding/json"
"fmt"
"image/png"
"io/ioutil"
"os"
"strings"
)
// TestScenario is the computer video state
type TestScenario struct {
VideoMode uint16 `json:"mode"`
VideoModeName string `json:"name"`
ScreenModes []int `json:"screens"`
TextPages [4][]uint8 `json:"text"`
VideoPages [4][]uint8 `json:"video"`
SVideoPage []uint8 `json:"svideo"`
}
func grabTestScenario(vs VideoSource) *TestScenario {
textPages := [4][]uint8{
cloneSlice(vs.GetTextMemory(false, false)),
cloneSlice(vs.GetTextMemory(false, true)),
cloneSlice(vs.GetTextMemory(true, false)),
cloneSlice(vs.GetTextMemory(true, true)),
}
videoPages := [4][]uint8{
cloneSlice(vs.GetVideoMemory(false, false)),
cloneSlice(vs.GetVideoMemory(false, true)),
cloneSlice(vs.GetVideoMemory(true, false)),
cloneSlice(vs.GetVideoMemory(true, true)),
}
knownModes := []int{
ScreenModeGreen,
ScreenModeNTSC,
ScreenModePlain,
}
return &TestScenario{
vs.GetCurrentVideoMode(),
VideoModeName(vs),
knownModes,
textPages,
videoPages,
cloneSlice(vs.GetSuperVideoMemory()),
}
}
func cloneSlice(src []uint8) []uint8 {
dst := make([]uint8, len(src))
copy(dst, src)
return dst
}
func loadTestScenario(filename string) (*TestScenario, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var ts TestScenario
err = json.Unmarshal(bytes, &ts)
if err != nil {
return nil, err
}
return &ts, nil
}
func (ts *TestScenario) save(dir string) (string, error) {
bytes, err := json.Marshal(ts)
if err != nil {
return "", err
}
pattern := fmt.Sprintf("%v_*.json", strings.ToLower(ts.VideoModeName))
file, err := ioutil.TempFile(dir, pattern)
if err != nil {
return "", err
}
defer file.Close()
_, err = file.Write(bytes)
return file.Name(), err
}
// GetCurrentVideoMode returns the active video mode
func (ts *TestScenario) GetCurrentVideoMode() uint16 {
return ts.VideoMode
}
func optionsToIndex(secondPage bool, ext bool) int {
index := 0
if secondPage {
index += 2
}
if ext {
index++
}
return index
}
// GetTextMemory returns a slice to the text memory pages
func (ts *TestScenario) GetTextMemory(secondPage bool, ext bool) []uint8 {
return ts.TextPages[optionsToIndex(secondPage, ext)]
}
// GetVideoMemory returns a slice to the video memory pages
func (ts *TestScenario) GetVideoMemory(secondPage bool, ext bool) []uint8 {
return ts.VideoPages[optionsToIndex(secondPage, ext)]
}
// GetCharacterPixel returns the pixel as output by the character generator
func (ts *TestScenario) GetCharacterPixel(char uint8, rowInChar int, colInChar int) bool {
// We don't have a character generator. We will return a square or blank for spaces
if char&0x3f == 0x20 {
return false // Space char
}
return !(rowInChar == 0 || rowInChar == 7 || colInChar == 0 || colInChar == 6)
}
// GetSuperVideoMemory returns a slice to the SHR video memory
func (ts *TestScenario) GetSuperVideoMemory() []uint8 {
return ts.SVideoPage
}
func buildImageName(name string, screenMode int, altSet bool) string {
var screenName string
switch screenMode {
case ScreenModeGreen:
screenName = "green"
case ScreenModeNTSC:
screenName = "ntsc"
case ScreenModePlain:
screenName = "plain"
default:
screenName = "unknown"
}
if altSet {
screenName += "_new"
}
return strings.TrimSuffix(name, ".json") +
screenName + ".png"
}
func (ts *TestScenario) generateSnapshots(baseName string, altSet bool) error {
for _, screen := range ts.ScreenModes {
image := Snapshot(ts, screen)
imageName := buildImageName(baseName, screen, altSet)
f, err := os.Create(imageName)
if err != nil {
return err
}
defer f.Close()
png.Encode(f, image)
}
return nil
}
// AddScenario Generate a new video scenario
func AddScenario(vs VideoSource, dir string) error {
// Get memory contents
ts := grabTestScenario(vs)
name, err := ts.save(dir)
if err != nil {
return err
}
return ts.generateSnapshots(name, false)
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B