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	"internal/obscuretestdata"
11	"io/ioutil"
12	"os"
13	"reflect"
14	"testing"
15)
16
17const (
18	expectedID = "abcdefghijklmnopqrstuvwxyz.1234567890123456789012345678901234567890123456789012345678901234"
19	newID      = "bcdefghijklmnopqrstuvwxyza.2345678901234567890123456789012345678901234567890123456789012341"
20)
21
22func TestReadFile(t *testing.T) {
23	f, err := ioutil.TempFile("", "buildid-test-")
24	if err != nil {
25		t.Fatal(err)
26	}
27	tmp := f.Name()
28	defer os.Remove(tmp)
29	f.Close()
30
31	// Use obscured files to prevent Apple’s notarization service from
32	// mistaking them as candidates for notarization and rejecting the entire
33	// toolchain.
34	// See golang.org/issue/34986
35	var files = []string{
36		"p.a.base64",
37		"a.elf.base64",
38		"a.macho.base64",
39		"a.pe.base64",
40	}
41
42	for _, name := range files {
43		f, err := obscuretestdata.DecodeToTempFile("testdata/" + name)
44		if err != nil {
45			t.Errorf("obscuretestdata.DecodeToTempFile(testdata/%s): %v", name, err)
46			continue
47		}
48		defer os.Remove(f)
49		id, err := ReadFile(f)
50		if id != expectedID || err != nil {
51			t.Errorf("ReadFile(testdata/%s) = %q, %v, want %q, nil", f, id, err, expectedID)
52		}
53		old := readSize
54		readSize = 2048
55		id, err = ReadFile(f)
56		readSize = old
57		if id != expectedID || err != nil {
58			t.Errorf("ReadFile(%s) [readSize=2k] = %q, %v, want %q, nil", f, id, err, expectedID)
59		}
60
61		data, err := ioutil.ReadFile(f)
62		if err != nil {
63			t.Fatal(err)
64		}
65		m, _, err := FindAndHash(bytes.NewReader(data), expectedID, 1024)
66		if err != nil {
67			t.Errorf("FindAndHash(%s): %v", f, err)
68			continue
69		}
70		if err := ioutil.WriteFile(tmp, data, 0666); err != nil {
71			t.Error(err)
72			continue
73		}
74		tf, err := os.OpenFile(tmp, os.O_WRONLY, 0)
75		if err != nil {
76			t.Error(err)
77			continue
78		}
79		err = Rewrite(tf, m, newID)
80		err2 := tf.Close()
81		if err != nil {
82			t.Errorf("Rewrite(%s): %v", f, err)
83			continue
84		}
85		if err2 != nil {
86			t.Fatal(err2)
87		}
88
89		id, err = ReadFile(tmp)
90		if id != newID || err != nil {
91			t.Errorf("ReadFile(%s after Rewrite) = %q, %v, want %q, nil", f, id, err, newID)
92		}
93	}
94}
95
96func TestFindAndHash(t *testing.T) {
97	buf := make([]byte, 64)
98	buf2 := make([]byte, 64)
99	id := make([]byte, 8)
100	zero := make([]byte, 8)
101	for i := range id {
102		id[i] = byte(i)
103	}
104	numError := 0
105	errorf := func(msg string, args ...interface{}) {
106		t.Errorf(msg, args...)
107		if numError++; numError > 20 {
108			t.Logf("stopping after too many errors")
109			t.FailNow()
110		}
111	}
112	for bufSize := len(id); bufSize <= len(buf); bufSize++ {
113		for j := range buf {
114			for k := 0; k < 2*len(id) && j+k < len(buf); k++ {
115				for i := range buf {
116					buf[i] = 1
117				}
118				copy(buf[j:], id)
119				copy(buf[j+k:], id)
120				var m []int64
121				if j+len(id) <= j+k {
122					m = append(m, int64(j))
123				}
124				if j+k+len(id) <= len(buf) {
125					m = append(m, int64(j+k))
126				}
127				copy(buf2, buf)
128				for _, p := range m {
129					copy(buf2[p:], zero)
130				}
131				h := sha256.Sum256(buf2)
132
133				matches, hash, err := FindAndHash(bytes.NewReader(buf), string(id), bufSize)
134				if err != nil {
135					errorf("bufSize=%d j=%d k=%d: findAndHash: %v", bufSize, j, k, err)
136					continue
137				}
138				if !reflect.DeepEqual(matches, m) {
139					errorf("bufSize=%d j=%d k=%d: findAndHash: matches=%v, want %v", bufSize, j, k, matches, m)
140					continue
141				}
142				if hash != h {
143					errorf("bufSize=%d j=%d k=%d: findAndHash: matches correct, but hash=%x, want %x", bufSize, j, k, hash, h)
144				}
145			}
146		}
147	}
148}
149