func testEndToEnd(t *testing.T, goarch string) { lex.InitHist() input := filepath.Join("testdata", goarch+".s") output := filepath.Join("testdata", goarch+".out") architecture, ctxt := setArch(goarch) lexer := lex.NewLexer(input, ctxt) parser := NewParser(ctxt, architecture, lexer) pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes -S output to this buffer. ctxt.Bso = obj.Binitw(os.Stdout) defer obj.Bflush(ctxt.Bso) ctxt.Diag = log.Fatalf obj.Binitw(ioutil.Discard) pList.Firstpc, ok = parser.Parse() if !ok { t.Fatalf("asm: %s assembly failed", goarch) } result := string(testOut.Bytes()) expect, err := ioutil.ReadFile(output) // For Windows. result = strings.Replace(result, `testdata\`, `testdata/`, -1) if err != nil { t.Fatal(err) } if result != string(expect) { if false { // Enable to capture output. fmt.Printf("%s", result) os.Exit(1) } t.Errorf("%s failed: output differs", goarch) r := strings.Split(result, "\n") e := strings.Split(string(expect), "\n") if len(r) != len(e) { t.Errorf("%s: expected %d lines, got %d", goarch, len(e), len(r)) } n := len(e) if n > len(r) { n = len(r) } for i := 0; i < n; i++ { if r[i] != e[i] { t.Errorf("%s:%d:\nexpected\n\t%s\ngot\n\t%s", output, i, e[i], r[i]) } } } }
func testEndToEnd(t *testing.T, goarch, file string) { lex.InitHist() input := filepath.Join("testdata", file+".s") architecture, ctxt := setArch(goarch) lexer := lex.NewLexer(input, ctxt) parser := NewParser(ctxt, architecture, lexer) pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. ctxt.Bso = obj.Binitw(os.Stdout) defer ctxt.Bso.Flush() failed := false ctxt.Diag = func(format string, args ...interface{}) { failed = true t.Errorf(format, args...) } obj.Binitw(ioutil.Discard) pList.Firstpc, ok = parser.Parse() if !ok || failed { t.Errorf("asm: %s assembly failed", goarch) return } output := strings.Split(testOut.String(), "\n") // Reconstruct expected output by independently "parsing" the input. data, err := ioutil.ReadFile(input) if err != nil { t.Error(err) return } lineno := 0 seq := 0 hexByLine := map[string]string{} lines := strings.SplitAfter(string(data), "\n") Diff: for _, line := range lines { lineno++ // The general form of a test input line is: // // comment // INST args [// printed form] [// hex encoding] parts := strings.Split(line, "//") printed := strings.TrimSpace(parts[0]) if printed == "" || strings.HasSuffix(printed, ":") { // empty or label continue } seq++ var hexes string switch len(parts) { default: t.Errorf("%s:%d: unable to understand comments: %s", input, lineno, line) case 1: // no comment case 2: // might be printed form or hex note := strings.TrimSpace(parts[1]) if isHexes(note) { hexes = note } else { printed = note } case 3: // printed form, then hex printed = strings.TrimSpace(parts[1]) hexes = strings.TrimSpace(parts[2]) if !isHexes(hexes) { t.Errorf("%s:%d: malformed hex instruction encoding: %s", input, lineno, line) } } if hexes != "" { hexByLine[fmt.Sprintf("%s:%d", input, lineno)] = hexes } // Canonicalize spacing in printed form. // First field is opcode, then tab, then arguments separated by spaces. // Canonicalize spaces after commas first. // Comma to separate argument gets a space; comma within does not. var buf []byte nest := 0 for i := 0; i < len(printed); i++ { c := printed[i] switch c { case '{', '[': nest++ case '}', ']': nest-- case ',': buf = append(buf, ',') if nest == 0 { buf = append(buf, ' ') } for i+1 < len(printed) && (printed[i+1] == ' ' || printed[i+1] == '\t') { i++ } continue } buf = append(buf, c) } f := strings.Fields(string(buf)) // Turn relative (PC) into absolute (PC) automatically, // so that most branch instructions don't need comments // giving the absolute form. if len(f) > 0 && strings.HasSuffix(printed, "(PC)") { last := f[len(f)-1] n, err := strconv.Atoi(last[:len(last)-len("(PC)")]) if err == nil { f[len(f)-1] = fmt.Sprintf("%d(PC)", seq+n) } } if len(f) == 1 { printed = f[0] } else { printed = f[0] + "\t" + strings.Join(f[1:], " ") } want := fmt.Sprintf("%05d (%s:%d)\t%s", seq, input, lineno, printed) for len(output) > 0 && (output[0] < want || output[0] != want && len(output[0]) >= 5 && output[0][:5] == want[:5]) { if len(output[0]) >= 5 && output[0][:5] == want[:5] { t.Errorf("mismatched output:\nhave %s\nwant %s", output[0], want) output = output[1:] continue Diff } t.Errorf("unexpected output: %q", output[0]) output = output[1:] } if len(output) > 0 && output[0] == want { output = output[1:] } else { t.Errorf("missing output: %q", want) } } for len(output) > 0 { if output[0] == "" { // spurious blank caused by Split on "\n" output = output[1:] continue } t.Errorf("unexpected output: %q", output[0]) output = output[1:] } // Checked printing. // Now check machine code layout. top := pList.Firstpc var text *obj.LSym ok = true ctxt.Diag = func(format string, args ...interface{}) { t.Errorf(format, args...) ok = false } obj.Flushplist(ctxt) for p := top; p != nil; p = p.Link { if p.As == obj.ATEXT { text = p.From.Sym } hexes := hexByLine[p.Line()] if hexes == "" { continue } delete(hexByLine, p.Line()) if text == nil { t.Errorf("%s: instruction outside TEXT", p) } size := int64(len(text.P)) - p.Pc if p.Link != nil { size = p.Link.Pc - p.Pc } else if p.Isize != 0 { size = int64(p.Isize) } var code []byte if p.Pc < int64(len(text.P)) { code = text.P[p.Pc:] if size < int64(len(code)) { code = code[:size] } } codeHex := fmt.Sprintf("%x", code) if codeHex == "" { codeHex = "empty" } ok := false for _, hex := range strings.Split(hexes, " or ") { if codeHex == hex { ok = true break } } if !ok { t.Errorf("%s: have encoding %s, want %s", p, codeHex, hexes) } } if len(hexByLine) > 0 { var missing []string for key := range hexByLine { missing = append(missing, key) } sort.Strings(missing) for _, line := range missing { t.Errorf("%s: did not find instruction encoding", line) } } }
func testErrors(t *testing.T, goarch, file string) { lex.InitHist() input := filepath.Join("testdata", file+".s") architecture, ctxt := setArch(goarch) lexer := lex.NewLexer(input, ctxt) parser := NewParser(ctxt, architecture, lexer) pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() failed := false var errBuf bytes.Buffer ctxt.DiagFunc = func(format string, args ...interface{}) { failed = true s := fmt.Sprintf(format, args...) if !strings.HasSuffix(s, "\n") { s += "\n" } errBuf.WriteString(s) } pList.Firstpc, ok = parser.Parse() obj.Flushplist(ctxt) if ok && !failed { t.Errorf("asm: %s had no errors", goarch) } errors := map[string]string{} for _, line := range strings.Split(errBuf.String(), "\n") { if line == "" || strings.HasPrefix(line, "\t") { continue } m := fileLineRE.FindStringSubmatch(line) if m == nil { t.Errorf("unexpected error: %v", line) continue } fileline := m[1] if errors[fileline] != "" { t.Errorf("multiple errors on %s:\n\t%s\n\t%s", fileline, errors[fileline], line) continue } errors[fileline] = line } // Reconstruct expected errors by independently "parsing" the input. data, err := ioutil.ReadFile(input) if err != nil { t.Error(err) return } lineno := 0 lines := strings.Split(string(data), "\n") for _, line := range lines { lineno++ fileline := fmt.Sprintf("%s:%d", input, lineno) if m := errRE.FindStringSubmatch(line); m != nil { all := m[1] mm := errQuotesRE.FindAllStringSubmatch(all, -1) if len(mm) != 1 { t.Errorf("%s: invalid errorcheck line:\n%s", fileline, line) } else if err := errors[fileline]; err == "" { t.Errorf("%s: missing error, want %s", fileline, all) } else if !strings.Contains(err, mm[0][1]) { t.Errorf("%s: wrong error for %s:\n%s", fileline, all, err) } } else { if errors[fileline] != "" { t.Errorf("unexpected error on %s: %v", fileline, errors[fileline]) } } delete(errors, fileline) } var extra []string for key := range errors { extra = append(extra, key) } sort.Strings(extra) for _, fileline := range extra { t.Errorf("unexpected error on %s: %v", fileline, errors[fileline]) } }