Home | History | Annotate | Download | only in proftest
      1 // Copyright 2014 Google Inc. All Rights Reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // Package proftest provides some utility routines to test other
     16 // packages related to profiles.
     17 package proftest
     18 
     19 import (
     20 	"encoding/json"
     21 	"fmt"
     22 	"io/ioutil"
     23 	"os"
     24 	"os/exec"
     25 	"regexp"
     26 	"testing"
     27 )
     28 
     29 // Diff compares two byte arrays using the diff tool to highlight the
     30 // differences. It is meant for testing purposes to display the
     31 // differences between expected and actual output.
     32 func Diff(b1, b2 []byte) (data []byte, err error) {
     33 	f1, err := ioutil.TempFile("", "proto_test")
     34 	if err != nil {
     35 		return nil, err
     36 	}
     37 	defer os.Remove(f1.Name())
     38 	defer f1.Close()
     39 
     40 	f2, err := ioutil.TempFile("", "proto_test")
     41 	if err != nil {
     42 		return nil, err
     43 	}
     44 	defer os.Remove(f2.Name())
     45 	defer f2.Close()
     46 
     47 	f1.Write(b1)
     48 	f2.Write(b2)
     49 
     50 	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
     51 	if len(data) > 0 {
     52 		// diff exits with a non-zero status when the files don't match.
     53 		// Ignore that failure as long as we get output.
     54 		err = nil
     55 	}
     56 	if err != nil {
     57 		data = []byte(fmt.Sprintf("diff failed: %v\nb1: %q\nb2: %q\n", err, b1, b2))
     58 		err = nil
     59 	}
     60 	return
     61 }
     62 
     63 // EncodeJSON encodes a value into a byte array. This is intended for
     64 // testing purposes.
     65 func EncodeJSON(x interface{}) []byte {
     66 	data, err := json.MarshalIndent(x, "", "    ")
     67 	if err != nil {
     68 		panic(err)
     69 	}
     70 	data = append(data, '\n')
     71 	return data
     72 }
     73 
     74 // TestUI implements the plugin.UI interface, triggering test failures
     75 // if more than Ignore errors not matching AllowRx are printed.
     76 // Also tracks the number of times the error matches AllowRx in
     77 // NumAllowRxMatches.
     78 type TestUI struct {
     79 	T                 *testing.T
     80 	Ignore            int
     81 	AllowRx           string
     82 	NumAllowRxMatches int
     83 }
     84 
     85 // ReadLine returns no input, as no input is expected during testing.
     86 func (ui *TestUI) ReadLine(_ string) (string, error) {
     87 	return "", fmt.Errorf("no input")
     88 }
     89 
     90 // Print messages are discarded by the test UI.
     91 func (ui *TestUI) Print(args ...interface{}) {
     92 }
     93 
     94 // PrintErr messages may trigger an error failure. A fixed number of
     95 // error messages are permitted when appropriate.
     96 func (ui *TestUI) PrintErr(args ...interface{}) {
     97 	if ui.AllowRx != "" {
     98 		if matched, err := regexp.MatchString(ui.AllowRx, fmt.Sprint(args...)); matched || err != nil {
     99 			if err != nil {
    100 				ui.T.Errorf("failed to match against regex %q: %v", ui.AllowRx, err)
    101 			}
    102 			ui.NumAllowRxMatches++
    103 			return
    104 		}
    105 	}
    106 	if ui.Ignore > 0 {
    107 		ui.Ignore--
    108 		return
    109 	}
    110 	// Stringify arguments with fmt.Sprint() to match what default UI
    111 	// implementation does. Without this Error() calls fmt.Sprintln() which
    112 	// _always_ adds spaces between arguments, unlike fmt.Sprint() which only
    113 	// adds them between arguments if neither is string.
    114 	ui.T.Error(fmt.Sprint(args...))
    115 }
    116 
    117 // IsTerminal indicates if the UI is an interactive terminal.
    118 func (ui *TestUI) IsTerminal() bool {
    119 	return false
    120 }
    121 
    122 // SetAutoComplete is not supported by the test UI.
    123 func (ui *TestUI) SetAutoComplete(_ func(string) string) {
    124 }
    125