1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package buildid
6
7import (
8	"bytes"
9	"crypto/sha256"
10	"io/ioutil"
11	"os"
12	"reflect"
13	"testing"
14)
15
16const (
17	expectedID = "abcdefghijklmnopqrstuvwxyz.1234567890123456789012345678901234567890123456789012345678901234"
18	newID      = "bcdefghijklmnopqrstuvwxyza.2345678901234567890123456789012345678901234567890123456789012341"
19)
20
21func TestReadFile(t *testing.T) {
22	var files = []string{
23		"p.a",
24		"a.elf",
25		"a.macho",
26		"a.pe",
27	}
28
29	f, err := ioutil.TempFile("", "buildid-test-")
30	if err != nil {
31		t.Fatal(err)
32	}
33	tmp := f.Name()
34	defer os.Remove(tmp)
35	f.Close()
36
37	for _, f := range files {
38		id, err := ReadFile("testdata/" + f)
39		if id != expectedID || err != nil {
40			t.Errorf("ReadFile(testdata/%s) = %q, %v, want %q, nil", f, id, err, expectedID)
41		}
42		old := readSize
43		readSize = 2048
44		id, err = ReadFile("testdata/" + f)
45		readSize = old
46		if id != expectedID || err != nil {
47			t.Errorf("ReadFile(testdata/%s) [readSize=2k] = %q, %v, want %q, nil", f, id, err, expectedID)
48		}
49
50		data, err := ioutil.ReadFile("testdata/" + f)
51		if err != nil {
52			t.Fatal(err)
53		}
54		m, _, err := FindAndHash(bytes.NewReader(data), expectedID, 1024)
55		if err != nil {
56			t.Errorf("FindAndHash(testdata/%s): %v", f, err)
57			continue
58		}
59		if err := ioutil.WriteFile(tmp, data, 0666); err != nil {
60			t.Error(err)
61			continue
62		}
63		tf, err := os.OpenFile(tmp, os.O_WRONLY, 0)
64		if err != nil {
65			t.Error(err)
66			continue
67		}
68		err = Rewrite(tf, m, newID)
69		err2 := tf.Close()
70		if err != nil {
71			t.Errorf("Rewrite(testdata/%s): %v", f, err)
72			continue
73		}
74		if err2 != nil {
75			t.Fatal(err2)
76		}
77
78		id, err = ReadFile(tmp)
79		if id != newID || err != nil {
80			t.Errorf("ReadFile(testdata/%s after Rewrite) = %q, %v, want %q, nil", f, id, err, newID)
81		}
82	}
83}
84
85func TestFindAndHash(t *testing.T) {
86	buf := make([]byte, 64)
87	buf2 := make([]byte, 64)
88	id := make([]byte, 8)
89	zero := make([]byte, 8)
90	for i := range id {
91		id[i] = byte(i)
92	}
93	numError := 0
94	errorf := func(msg string, args ...interface{}) {
95		t.Errorf(msg, args...)
96		if numError++; numError > 20 {
97			t.Logf("stopping after too many errors")
98			t.FailNow()
99		}
100	}
101	for bufSize := len(id); bufSize <= len(buf); bufSize++ {
102		for j := range buf {
103			for k := 0; k < 2*len(id) && j+k < len(buf); k++ {
104				for i := range buf {
105					buf[i] = 1
106				}
107				copy(buf[j:], id)
108				copy(buf[j+k:], id)
109				var m []int64
110				if j+len(id) <= j+k {
111					m = append(m, int64(j))
112				}
113				if j+k+len(id) <= len(buf) {
114					m = append(m, int64(j+k))
115				}
116				copy(buf2, buf)
117				for _, p := range m {
118					copy(buf2[p:], zero)
119				}
120				h := sha256.Sum256(buf2)
121
122				matches, hash, err := FindAndHash(bytes.NewReader(buf), string(id), bufSize)
123				if err != nil {
124					errorf("bufSize=%d j=%d k=%d: findAndHash: %v", bufSize, j, k, err)
125					continue
126				}
127				if !reflect.DeepEqual(matches, m) {
128					errorf("bufSize=%d j=%d k=%d: findAndHash: matches=%v, want %v", bufSize, j, k, matches, m)
129					continue
130				}
131				if hash != h {
132					errorf("bufSize=%d j=%d k=%d: findAndHash: matches correct, but hash=%x, want %x", bufSize, j, k, hash, h)
133				}
134			}
135		}
136	}
137}
138