blob: e2f5b24de60eab596dfa330f1831c8a34cb3c8c2 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
func newTestContext(t testing.TB) *context {
ctx := &context{xedPath: filepath.Join("testdata", "xedpath")}
db, err := xeddata.NewDatabase(ctx.xedPath)
if err != nil {
t.Fatalf("open test database: %v", err)
ctx.db = db
return ctx
func newStringSet(keys ...string) map[string]bool {
set := make(map[string]bool)
for _, k := range keys {
set[k] = true
return set
func generateToString(t *testing.T) string {
ctx := newTestContext(t)
var buf bytes.Buffer
writeTables(&buf, ctx)
return buf.String()
func TestOutput(t *testing.T) {
// Ytab lists and optabs output checks.
// These tests are very fragile.
// Slight changes can invalidate them.
// It is better to keep testCases count at the minimum.
type testCase struct {
opcode string
ytabs string
optabLines string
var testCases []testCase
opcodeRE := regexp.MustCompile(`as: ([A-Z][A-Z0-9]*)`)
data, err := ioutil.ReadFile(filepath.Join("testdata", "golden.txt"))
if err != nil {
t.Fatalf("read golden file: %v", err)
for _, entry := range bytes.Split(data, []byte("======")) {
parts := bytes.Split(entry, []byte("----"))
ytabs := parts[0]
optabLines := parts[1]
opcode := opcodeRE.FindSubmatch(optabLines)[1]
testCases = append(testCases, testCase{
ytabs: strings.TrimSpace(string(ytabs)),
optabLines: strings.TrimSpace(string(optabLines)),
opcode: string(opcode)[len("A"):],
output := generateToString(t)
for _, tc := range testCases {
if !strings.Contains(output, tc.ytabs) {
t.Errorf("%s: ytabs not matched", tc.opcode)
if !strings.Contains(output, tc.optabLines) {
t.Errorf("%s: optab lines not matched", tc.opcode)
func TestOutputStability(t *testing.T) {
// Generate output count+1 times and check that every time
// it is exactly the same string.
// The output should be deterministic to avoid unwanted diffs
// between each code generation.
const count = 8
want := generateToString(t)
var wg sync.WaitGroup
for i := 0; i < count; i++ {
go func(i int) {
if want != generateToString(t) {
t.Errorf("output #%d mismatches", i)
func TestOpcodeCoverage(t *testing.T) {
// Check that generator produces all expected opcodes from testdata files.
// All opcodes are in Go syntax.
// VEX/EVEX opcodes collected from XED-based x86.csv.
expectedOpcodes := newStringSet(
// AMD-specific VEX opcodes.
// Excluded from x86avxgen output for now.
amdOpcodes := newStringSet(
ctx := newTestContext(t)
for op := range amdOpcodes {
delete(expectedOpcodes, op)
for op := range ctx.optabs {
delete(expectedOpcodes, op)
for op := range expectedOpcodes {
t.Errorf("missing opcode: %s", op)