boringssl/util/all_tests.go
Adam Langley d7c5dfb233 Add native support for onces and thread-local storage.
Historically, OpenSSL has used callbacks for anything thread related,
but we don't actually have that many threading libraries to worry about:
just pthreads and Windows (I hope).

That suggests that it's quite reasonable to handle threading ourselves,
and eliminate the need for users to remember to install the thread
callbacks.

The first user of this would be ERR, which currently simulates
thread-local storage using a lock around a hash table keyed by the TID.
(Although I suspect that change will need some CMake work in order that
libpthread is automatically included with libcrypto when linking tests
etc, but not on Windows and without lots of ifs.)

Change-Id: I4dd088e3794506747f875c1f3e92b9bc6700fad2
Reviewed-on: https://boringssl-review.googlesource.com/4010
Reviewed-by: Adam Langley <agl@google.com>
2015-03-31 22:37:12 +00:00

232 lines
8.2 KiB
Go

/* Copyright (c) 2015, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"os"
"os/exec"
"path"
"strings"
"time"
)
// TODO(davidben): Link tests with the malloc shim and port -malloc-test to this runner.
var (
useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
buildDir = flag.String("build-dir", "build", "The build directory to run the tests from.")
jsonOutput = flag.String("json-output", "", "The file to output JSON results to.")
)
type test []string
var tests = []test{
{"crypto/base64/base64_test"},
{"crypto/bio/bio_test"},
{"crypto/bn/bn_test"},
{"crypto/bytestring/bytestring_test"},
{"crypto/cipher/aead_test", "aes-128-gcm", "crypto/cipher/test/aes_128_gcm_tests.txt"},
{"crypto/cipher/aead_test", "aes-128-key-wrap", "crypto/cipher/test/aes_128_key_wrap_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-gcm", "crypto/cipher/test/aes_256_gcm_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-key-wrap", "crypto/cipher/test/aes_256_key_wrap_tests.txt"},
{"crypto/cipher/aead_test", "chacha20-poly1305", "crypto/cipher/test/chacha20_poly1305_tests.txt"},
{"crypto/cipher/aead_test", "rc4-md5-tls", "crypto/cipher/test/rc4_md5_tls_tests.txt"},
{"crypto/cipher/aead_test", "rc4-sha1-tls", "crypto/cipher/test/rc4_sha1_tls_tests.txt"},
{"crypto/cipher/aead_test", "aes-128-cbc-sha1-tls", "crypto/cipher/test/aes_128_cbc_sha1_tls_tests.txt"},
{"crypto/cipher/aead_test", "aes-128-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt"},
{"crypto/cipher/aead_test", "aes-128-cbc-sha256-tls", "crypto/cipher/test/aes_128_cbc_sha256_tls_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-cbc-sha1-tls", "crypto/cipher/test/aes_256_cbc_sha1_tls_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/aes_256_cbc_sha1_tls_implicit_iv_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-cbc-sha256-tls", "crypto/cipher/test/aes_256_cbc_sha256_tls_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-cbc-sha384-tls", "crypto/cipher/test/aes_256_cbc_sha384_tls_tests.txt"},
{"crypto/cipher/aead_test", "des-ede3-cbc-sha1-tls", "crypto/cipher/test/des_ede3_cbc_sha1_tls_tests.txt"},
{"crypto/cipher/aead_test", "des-ede3-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/des_ede3_cbc_sha1_tls_implicit_iv_tests.txt"},
{"crypto/cipher/aead_test", "rc4-md5-ssl3", "crypto/cipher/test/rc4_md5_ssl3_tests.txt"},
{"crypto/cipher/aead_test", "rc4-sha1-ssl3", "crypto/cipher/test/rc4_sha1_ssl3_tests.txt"},
{"crypto/cipher/aead_test", "aes-128-cbc-sha1-ssl3", "crypto/cipher/test/aes_128_cbc_sha1_ssl3_tests.txt"},
{"crypto/cipher/aead_test", "aes-256-cbc-sha1-ssl3", "crypto/cipher/test/aes_256_cbc_sha1_ssl3_tests.txt"},
{"crypto/cipher/aead_test", "des-ede3-cbc-sha1-ssl3", "crypto/cipher/test/des_ede3_cbc_sha1_ssl3_tests.txt"},
{"crypto/cipher/aead_test", "aes-128-ctr-hmac-sha256", "crypto/cipher/test/aes_128_ctr_hmac_sha256.txt"},
{"crypto/cipher/aead_test", "aes-256-ctr-hmac-sha256", "crypto/cipher/test/aes_256_ctr_hmac_sha256.txt"},
{"crypto/cipher/cipher_test", "crypto/cipher/test/cipher_test.txt"},
{"crypto/constant_time_test"},
{"crypto/dh/dh_test"},
{"crypto/digest/digest_test"},
{"crypto/dsa/dsa_test"},
{"crypto/ec/ec_test"},
{"crypto/ec/example_mul"},
{"crypto/ecdsa/ecdsa_test"},
{"crypto/err/err_test"},
{"crypto/evp/evp_test"},
{"crypto/evp/pbkdf_test"},
{"crypto/hkdf/hkdf_test"},
{"crypto/hmac/hmac_test"},
{"crypto/lhash/lhash_test"},
{"crypto/modes/gcm_test"},
{"crypto/pkcs8/pkcs12_test"},
{"crypto/rsa/rsa_test"},
{"crypto/thread_test"},
{"crypto/x509/pkcs7_test"},
{"crypto/x509v3/tab_test"},
{"crypto/x509v3/v3name_test"},
{"ssl/pqueue/pqueue_test"},
{"ssl/ssl_test"},
}
// testOutput is a representation of Chromium's JSON test result format. See
// https://www.chromium.org/developers/the-json-test-results-format
type testOutput struct {
Version int `json:"version"`
Interrupted bool `json:"interrupted"`
PathDelimiter string `json:"path_delimiter"`
SecondsSinceEpoch float64 `json:"seconds_since_epoch"`
NumFailuresByType map[string]int `json:"num_failures_by_type"`
Tests map[string]testResult `json:"tests"`
}
type testResult struct {
Actual string `json:"actual"`
Expected string `json:"expected"`
}
func newTestOutput() *testOutput {
return &testOutput{
Version: 3,
PathDelimiter: ".",
SecondsSinceEpoch: float64(time.Now().UnixNano()) / float64(time.Second/time.Nanosecond),
NumFailuresByType: make(map[string]int),
Tests: make(map[string]testResult),
}
}
func (t *testOutput) addResult(name, result string) {
if _, found := t.Tests[name]; found {
panic(name)
}
t.Tests[name] = testResult{Actual: result, Expected: "PASS"}
t.NumFailuresByType[result]++
}
func (t *testOutput) writeTo(name string) error {
file, err := os.Create(name)
if err != nil {
return err
}
defer file.Close()
out, err := json.MarshalIndent(t, "", " ")
if err != nil {
return err
}
_, err = file.Write(out)
return err
}
func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
if dbAttach {
valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
}
valgrindArgs = append(valgrindArgs, path)
valgrindArgs = append(valgrindArgs, args...)
return exec.Command("valgrind", valgrindArgs...)
}
func runTest(test test) (passed bool, err error) {
prog := path.Join(*buildDir, test[0])
args := test[1:]
var cmd *exec.Cmd
if *useValgrind {
cmd = valgrindOf(false, prog, args...)
} else {
cmd = exec.Command(prog, args...)
}
var stdoutBuf bytes.Buffer
cmd.Stdout = &stdoutBuf
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return false, err
}
if err := cmd.Wait(); err != nil {
return false, err
}
// Account for Windows line-endings.
stdout := bytes.Replace(stdoutBuf.Bytes(), []byte("\r\n"), []byte("\n"), -1)
if bytes.HasSuffix(stdout, []byte("PASS\n")) &&
(len(stdout) == 5 || stdout[len(stdout)-6] == '\n') {
return true, nil
}
return false, nil
}
// shortTestName returns the short name of a test. It assumes that any argument
// which ends in .txt is a path to a data file and not relevant to the test's
// uniqueness.
func shortTestName(test test) string {
var args []string
for _, arg := range test {
if !strings.HasSuffix(arg, ".txt") {
args = append(args, arg)
}
}
return strings.Join(args, " ")
}
func main() {
flag.Parse()
testOutput := newTestOutput()
var failed []test
for _, test := range tests {
fmt.Printf("%s\n", strings.Join([]string(test), " "))
name := shortTestName(test)
passed, err := runTest(test)
if err != nil {
fmt.Printf("%s failed to complete: %s\n", test[0], err)
failed = append(failed, test)
testOutput.addResult(name, "CRASHED")
} else if !passed {
fmt.Printf("%s failed to print PASS on the last line.\n", test[0])
failed = append(failed, test)
testOutput.addResult(name, "FAIL")
} else {
testOutput.addResult(name, "PASS")
}
}
if len(failed) == 0 {
fmt.Printf("\nAll tests passed!\n")
} else {
fmt.Printf("\n%d of %d tests failed:\n", len(failed), len(tests))
for _, test := range failed {
fmt.Printf("\t%s\n", strings.Join([]string(test), " "))
}
}
if *jsonOutput != "" {
if err := testOutput.writeTo(*jsonOutput); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
}
}
}