<commit_before>/*
 * Minio Client (C) 2014, 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/client"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// rm specific flags.
var (
	rmFlagForce = cli.BoolFlag{
		Name:  "force",
		Usage: "Force a dangerous remove operation.",
	}

	rmFlagIncomplete = cli.BoolFlag{
		Name:  "incomplete, I",
		Usage: "Remove an incomplete upload(s).",
	}

	rmFlagHelp = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Help of rm.",
	}
)

// remove a file or folder.
var rmCmd = cli.Command{
	Name:   "rm",
	Usage:  "Remove file or bucket [WARNING: Use with care].",
	Action: mainRm,
	Flags:  []cli.Flag{rmFlagForce, rmFlagIncomplete, rmFlagHelp},
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] TARGET [TARGET ...]

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Remove an object.
     $ mc {{.Name}} https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   2. Remove a file.
      $ mc {{.Name}} 1999/old-backup.tgz

   3. Remove contents of a folder recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs/louis/...

   4  Remove contents of a folder recursively and folder itself.
      $ mc {{.Name}} old/photos...

   5. Remove a bucket and all its contents recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs...

   6. Remove all matching objects with this prefix.
     $ mc {{.Name}} --force https://s3.amazonaws.com/ogg/gunmetal...

   7. Cancel an incomplete upload of an object.
      $ mc {{.Name}} --incomplete https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   8. Remove all incomplete uploads recursively matching this prefix.
      $ mc {{.Name}} --incomplete --force https://s3.amazonaws.com/jazz-songs/louis/...
`,
}

// Structured message depending on the type of console.
type rmMessage struct {
	URL string `json:"url"`
}

// Colorized message for console printing.
func (r rmMessage) String() string {
	return console.Colorize("Remove", fmt.Sprintf("Removed ‘%s’.", r.URL))
}

// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
	msgBytes, e := json.Marshal(r)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(msgBytes)
}

// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
	args := ctx.Args()
	isForce := ctx.Bool("force")

	if !args.Present() {
		exitCode := 1
		cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
	}

	URLs, err := args2URLs(args)
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// If input validation fails then provide context sensitive help without displaying generic help message.
	// The context sensitive help is shown per argument instead of all arguments to keep the help display
	// as well as the code simple. Also most of the times there will be just one arg
	for _, url := range URLs {
		u := client.NewURL(url)
		if strings.HasSuffix(url, string(u.Separator)) {
			fatalIf(errDummy().Trace(),
				"‘"+url+"’ is a folder. To remove this folder recursively, please try ‘"+url+"...’ as argument.")
		}
		if isURLRecursive(url) && !isForce {
			fatalIf(errDummy().Trace(),
				"Recursive removal requires --force option. Please review carefully before performing this operation.")
		}
	}
}

// Remove a single object.
func rm(url string, isIncomplete bool) *probe.Error {
	clnt, err := url2Client(url)
	if err != nil {
		return err.Trace(url)
	}

	if err = clnt.Remove(isIncomplete); err != nil {
		return err.Trace(url)
	}
	return nil
}

// Remove all objects recursively.
func rmAll(url string, isIncomplete bool) {
	// Initialize new client.
	clnt, err := url2Client(url)
	if err != nil {
		errorIf(err.Trace(url), "Invalid URL ‘"+url+"’.")
		return // End of journey.
	}
	isRecursive := false // Disable recursion and only list this folder's contents.
	for entry := range clnt.List(isRecursive, isIncomplete) {
		if entry.Err != nil {
			errorIf(entry.Err.Trace(url), "Unable to list ‘"+url+"’.")
			return // End of journey.
		}

		if entry.Type.IsDir() {
			// Add separator at the end to remove all its contents.
			url := entry.URL
			url.Path = strings.TrimSuffix(entry.URL.Path, string(entry.URL.Separator)) + string(entry.URL.Separator)

			// Recursively remove contents of this directory.
			rmAll(url.String(), isIncomplete)
		}
		// Regular type.
		err = rm(entry.URL.String(), isIncomplete)
		if err != nil {
			errorIf(err.Trace(entry.URL.String()), "Unable to remove ‘"+entry.URL.String()+"’.")
			continue
		}
		printMsg(rmMessage{entry.URL.String()})
	}
}

// main for rm command.
func mainRm(ctx *cli.Context) {
	checkRmSyntax(ctx)

	// rm specific flags.
	isForce := ctx.Bool("force")
	isIncomplete := ctx.Bool("incomplete")

	// Set color.
	console.SetColor("Remove", color.New(color.FgGreen, color.Bold))

	// Parse args.
	URLs, err := args2URLs(ctx.Args())
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// Support multiple targets.
	for _, url := range URLs {
		if isURLRecursive(url) && isForce {
			url := stripRecursiveURL(url)
			removeTopFolder := false
			// find if the URL is dir or not.
			_, content, err := url2Stat(url)
			if err != nil && !prefixExists(url) {
				fatalIf(err.Trace(url), "Unable to stat ‘"+url+"’.")
			}

			if err == nil && content.Type.IsDir() {
				// Determine whether to remove the top folder or only its
				// contents. If the URL does not end with a separator, then
				// include the top folder as well, otherwise not.
				u := client.NewURL(url)
				if !strings.HasSuffix(url, string(u.Separator)) {
					// Add separator at the end to remove all its contents.
					url = url + string(u.Separator)
					// Remember to remove the top most folder.
					removeTopFolder = true
				}
			}
			// Remove contents of this folder.
			rmAll(url, isIncomplete)
			// url will be '.' for arguments which will be just '...'
			if removeTopFolder && url != "." && url != ".." {
				// Remove top folder as well.
				err := rm(url, isIncomplete)
				if err != nil {
					errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
					continue
				}
				printMsg(rmMessage{url})
			}

		} else {
			err := rm(url, isIncomplete)
			if err != nil {
				errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
				continue
			}
			printMsg(rmMessage{url})
		}
	}
}
<commit_msg>rm: Fix wasn't proper in previous commit fix it<commit_after>/*
 * Minio Client (C) 2014, 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/client"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// rm specific flags.
var (
	rmFlagForce = cli.BoolFlag{
		Name:  "force",
		Usage: "Force a dangerous remove operation.",
	}

	rmFlagIncomplete = cli.BoolFlag{
		Name:  "incomplete, I",
		Usage: "Remove an incomplete upload(s).",
	}

	rmFlagHelp = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Help of rm.",
	}
)

// remove a file or folder.
var rmCmd = cli.Command{
	Name:   "rm",
	Usage:  "Remove file or bucket [WARNING: Use with care].",
	Action: mainRm,
	Flags:  []cli.Flag{rmFlagForce, rmFlagIncomplete, rmFlagHelp},
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] TARGET [TARGET ...]

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Remove an object.
     $ mc {{.Name}} https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   2. Remove a file.
      $ mc {{.Name}} 1999/old-backup.tgz

   3. Remove contents of a folder recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs/louis/...

   4  Remove contents of a folder recursively and folder itself.
      $ mc {{.Name}} old/photos...

   5. Remove a bucket and all its contents recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs...

   6. Remove all matching objects with this prefix.
     $ mc {{.Name}} --force https://s3.amazonaws.com/ogg/gunmetal...

   7. Cancel an incomplete upload of an object.
      $ mc {{.Name}} --incomplete https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   8. Remove all incomplete uploads recursively matching this prefix.
      $ mc {{.Name}} --incomplete --force https://s3.amazonaws.com/jazz-songs/louis/...
`,
}

// Structured message depending on the type of console.
type rmMessage struct {
	URL string `json:"url"`
}

// Colorized message for console printing.
func (r rmMessage) String() string {
	return console.Colorize("Remove", fmt.Sprintf("Removed ‘%s’.", r.URL))
}

// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
	msgBytes, e := json.Marshal(r)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(msgBytes)
}

// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
	args := ctx.Args()
	isForce := ctx.Bool("force")

	if !args.Present() {
		exitCode := 1
		cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
	}

	URLs, err := args2URLs(args)
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// If input validation fails then provide context sensitive help without displaying generic help message.
	// The context sensitive help is shown per argument instead of all arguments to keep the help display
	// as well as the code simple. Also most of the times there will be just one arg
	for _, url := range URLs {
		u := client.NewURL(url)
		if strings.HasSuffix(url, string(u.Separator)) {
			fatalIf(errDummy().Trace(),
				"‘"+url+"’ is a folder. To remove this folder recursively, please try ‘"+url+"...’ as argument.")
		}
		if isURLRecursive(url) && !isForce {
			fatalIf(errDummy().Trace(),
				"Recursive removal requires --force option. Please review carefully before performing this operation.")
		}
	}
}

// Remove a single object.
func rm(url string, isIncomplete bool) *probe.Error {
	clnt, err := url2Client(url)
	if err != nil {
		return err.Trace(url)
	}

	if err = clnt.Remove(isIncomplete); err != nil {
		return err.Trace(url)
	}
	return nil
}

// Remove all objects recursively.
func rmAll(url string, isIncomplete bool) {
	// Initialize new client.
	clnt, err := url2Client(url)
	if err != nil {
		errorIf(err.Trace(url), "Invalid URL ‘"+url+"’.")
		return // End of journey.
	}
	isRecursive := false // Disable recursion and only list this folder's contents.
	for entry := range clnt.List(isRecursive, isIncomplete) {
		if entry.Err != nil {
			errorIf(entry.Err.Trace(url), "Unable to list ‘"+url+"’.")
			return // End of journey.
		}

		if entry.Type.IsDir() {
			// Add separator at the end to remove all its contents.
			url := entry.URL
			url.Path = strings.TrimSuffix(entry.URL.Path, string(entry.URL.Separator)) + string(entry.URL.Separator)

			// Recursively remove contents of this directory.
			rmAll(url.String(), isIncomplete)
		}
		// Regular type.
		err = rm(entry.URL.String(), isIncomplete)
		if err != nil {
			errorIf(err.Trace(entry.URL.String()), "Unable to remove ‘"+entry.URL.String()+"’.")
			continue
		}
		printMsg(rmMessage{entry.URL.String()})
	}
}

// main for rm command.
func mainRm(ctx *cli.Context) {
	checkRmSyntax(ctx)

	// rm specific flags.
	isForce := ctx.Bool("force")
	isIncomplete := ctx.Bool("incomplete")

	// Set color.
	console.SetColor("Remove", color.New(color.FgGreen, color.Bold))

	// Parse args.
	URLs, err := args2URLs(ctx.Args())
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// Support multiple targets.
	for _, url := range URLs {
		if isURLRecursive(url) && isForce {
			url := stripRecursiveURL(url)
			removeTopFolder := false
			// find if the URL is dir or not.
			_, content, err := url2Stat(url)
			if err != nil && !prefixExists(url) {
				fatalIf(err.Trace(url), "Unable to stat ‘"+url+"’.")
			}

			if err == nil && content.Type.IsDir() {
				// Determine whether to remove the top folder or only its
				// contents. If the URL does not end with a separator, then
				// include the top folder as well, otherwise not.
				u := client.NewURL(url)
				if !strings.HasSuffix(url, string(u.Separator)) {
					// Add separator at the end to remove all its contents.
					url = url + string(u.Separator)
					// Remember to remove the top most folder.
					removeTopFolder = true
				}
			}
			// Remove contents of this folder.
			rmAll(url, isIncomplete)
			// Ignore url's './', '../' .
			if removeTopFolder && url != "./" && url != "../" {
				// Remove top folder as well.
				err := rm(url, isIncomplete)
				if err != nil {
					errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
					continue
				}
				printMsg(rmMessage{url})
			}

		} else {
			err := rm(url, isIncomplete)
			if err != nil {
				errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
				continue
			}
			printMsg(rmMessage{url})
		}
	}
}
<|endoftext|>


<commit_before>// Package transaction deals with the back and forth between the master and
// slave, and keeps track of the different tims and offsets that they give each
// other
//
// A transaction consists of a set of multiple messages between the slave and
// master, each containing the current time and their diff from the time
// delivered previously. Using this data an average offset between the two can
// be determined, taking into account RTT between the two endpoints.
//
// A transaction is started when a slave calls Initiate to set up a new
// transaction and receive the first message it should send to the master. Once
// the master sends back a response, this should be given to IncomingMessage,
// which will return another message to send to the master, and so on.
//
// On the master's side, when it receives a message from any slave it should
// pass that message into IncomingMessage, which will set up the necessary
// transaction data if necessary and return a response message. If it returns
// nil the transaction is completed and nothing needs to be sent.
package transaction

import (
	"time"

	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/saturn/config"
)

// To be called in order to start any necessary go-routines needed for this
// package to work
func Init() {
	// This is not automatically called (like init), and we want it that way so
	// we have the option of not starting txSpin during testing
	go txSpin()
}

var cleanupTimeout = 1 * time.Minute

type tx struct {
	id          string
	name        string
	tripTimes   []time.Duration
	offsets     []time.Duration
	lastMessage time.Time
	expectedSeq int32
}

func txSpin() {
	transactions := map[string]*tx{}
	cleanTick := time.Tick(cleanupTimeout / 2)
	for {
		select {
		case im := <-incomingMsgCh:
			im.replyCh <- incoming(transactions, im.msg)
		case it := <-initiateTxCh:
			it.replyCh <- initiate(transactions, it.idStr, it.srcIP)
		case <-cleanTick:
			cleanTxs(transactions)
		}
	}
}

// this function is not thread-safe
func cleanTxs(transactions map[string]*tx) {
	for k, t := range transactions {
		if time.Since(t.lastMessage) < cleanupTimeout {
			continue
		}
		delete(transactions, k)
	}
}

// newTx creates a new tx struct for a transaction with the given remote
// address, where the remote address has the given name (optional). The tx will
// be already added to the transactions map when it is returned. Note that you
// should modify expectedSeq as necessary (depending on which side of the
// transaction you're looking at)
func newTx(transactions map[string]*tx, id, name string) *tx {
	t := &tx{
		id:          id,
		name:        name,
		lastMessage: time.Now(),
	}
	if config.IsMaster {
		t.tripTimes = make([]time.Duration, 0, config.Iterations+1)
		t.offsets = make([]time.Duration, 0, config.Iterations+1)
	}
	transactions[id] = t
	return t
}

func (t *tx) kv() llog.KV {
	return llog.KV{
		"txID":          t.id,
		"clientName":        t.name,
	}
}
<commit_msg>go fmt<commit_after>// Package transaction deals with the back and forth between the master and
// slave, and keeps track of the different tims and offsets that they give each
// other
//
// A transaction consists of a set of multiple messages between the slave and
// master, each containing the current time and their diff from the time
// delivered previously. Using this data an average offset between the two can
// be determined, taking into account RTT between the two endpoints.
//
// A transaction is started when a slave calls Initiate to set up a new
// transaction and receive the first message it should send to the master. Once
// the master sends back a response, this should be given to IncomingMessage,
// which will return another message to send to the master, and so on.
//
// On the master's side, when it receives a message from any slave it should
// pass that message into IncomingMessage, which will set up the necessary
// transaction data if necessary and return a response message. If it returns
// nil the transaction is completed and nothing needs to be sent.
package transaction

import (
	"time"

	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/saturn/config"
)

// To be called in order to start any necessary go-routines needed for this
// package to work
func Init() {
	// This is not automatically called (like init), and we want it that way so
	// we have the option of not starting txSpin during testing
	go txSpin()
}

var cleanupTimeout = 1 * time.Minute

type tx struct {
	id          string
	name        string
	tripTimes   []time.Duration
	offsets     []time.Duration
	lastMessage time.Time
	expectedSeq int32
}

func txSpin() {
	transactions := map[string]*tx{}
	cleanTick := time.Tick(cleanupTimeout / 2)
	for {
		select {
		case im := <-incomingMsgCh:
			im.replyCh <- incoming(transactions, im.msg)
		case it := <-initiateTxCh:
			it.replyCh <- initiate(transactions, it.idStr, it.srcIP)
		case <-cleanTick:
			cleanTxs(transactions)
		}
	}
}

// this function is not thread-safe
func cleanTxs(transactions map[string]*tx) {
	for k, t := range transactions {
		if time.Since(t.lastMessage) < cleanupTimeout {
			continue
		}
		delete(transactions, k)
	}
}

// newTx creates a new tx struct for a transaction with the given remote
// address, where the remote address has the given name (optional). The tx will
// be already added to the transactions map when it is returned. Note that you
// should modify expectedSeq as necessary (depending on which side of the
// transaction you're looking at)
func newTx(transactions map[string]*tx, id, name string) *tx {
	t := &tx{
		id:          id,
		name:        name,
		lastMessage: time.Now(),
	}
	if config.IsMaster {
		t.tripTimes = make([]time.Duration, 0, config.Iterations+1)
		t.offsets = make([]time.Duration, 0, config.Iterations+1)
	}
	transactions[id] = t
	return t
}

func (t *tx) kv() llog.KV {
	return llog.KV{
		"txID":       t.id,
		"clientName": t.name,
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"bytes"
	"fmt"
	"os"
	"testing"

	"github.com/thriftrw/thriftrw-go/compile"

	"github.com/stretchr/testify/assert"
	"github.com/uber/tchannel-go/testutils"
)

func TestGetRequest(t *testing.T) {
	method := mustParseFile(t, validThrift).Services["Simple"].Functions["foo"]

	tests := []struct {
		desc   string
		opts   RequestOptions
		errMsg string
	}{
		{
			desc:   "Invalid JSON",
			opts:   RequestOptions{RequestJSON: "{"},
			errMsg: "parse",
		},
		{
			desc:   "Invalid field in request input",
			opts:   RequestOptions{RequestJSON: `{"foo": "1"}`},
			errMsg: "not found",
		},
		{
			desc: "Valid request",
			opts: RequestOptions{},
		},
	}

	for _, tt := range tests {
		got, err := getRequest(tt.opts, method)
		if tt.errMsg == "" {
			assert.NoError(t, err, "%v", tt.desc)
			assert.NotNil(t, got, "%v: Invalid request")
			continue
		}

		if assert.Error(t, err, "%v", tt.desc) {
			assert.Nil(t, got, "%v: Error cases should not return any bytes", tt.desc)
			assert.Contains(t, err.Error(), tt.errMsg, "%v: invalid error", tt.desc)
		}
	}
}

func TestGetMethodSpec(t *testing.T) {
	tests := []struct {
		desc           string
		opts           RequestOptions
		overrideMethod string
		checkSpec      func(*compile.FunctionSpec)
		errMsg         string
		errMsgs        []string
	}{
		{
			desc:   "No thrift file specified",
			opts:   RequestOptions{},
			errMsg: "specify a Thrift file",
		},
		{
			desc:   "Thrift file can't be found",
			opts:   RequestOptions{ThriftFile: "/fake/file"},
			errMsg: "cannot find Thrift file",
		},
		{
			desc:    "Thrift file can't be parsed",
			opts:    RequestOptions{ThriftFile: "testdata/invalid.json"},
			errMsgs: []string{"could not parse Thrift", "parse error"},
		},
		{
			desc:   "Invalid Thrift method name",
			opts:   RequestOptions{ThriftFile: validThrift, MethodName: "A::B::C"},
			errMsg: "invalid Thrift method",
		},
		{
			desc:   "Invalid service name",
			opts:   RequestOptions{ThriftFile: validThrift, MethodName: "UnknownSvc::foo"},
			errMsg: "could not find service",
		},
		{
			desc:   "Invalid method name",
			opts:   RequestOptions{ThriftFile: validThrift, MethodName: "Simple::unknownMethod"},
			errMsg: "could not find method",
		},
		{
			desc:   "Health and method name",
			opts:   RequestOptions{Health: true, MethodName: "And::Another"},
			errMsg: errHealthAndMethod.Error(),
		},
		{
			desc:           "Health method",
			opts:           RequestOptions{Health: true},
			overrideMethod: "Meta::health",
			checkSpec: func(spec *compile.FunctionSpec) {
				assert.Equal(t, 0, len(spec.ArgsSpec), "health method should not have arguments")
				assert.NotNil(t, spec.ResultSpec.ReturnType, "health method should have a return")
			},
		},
		{
			desc: "Valid Thrift file and method name",
			opts: RequestOptions{ThriftFile: validThrift, MethodName: fooMethod},
		},
	}

	for _, tt := range tests {
		expectedErrs := append([]string(nil), tt.errMsgs...)
		if tt.errMsg != "" {
			expectedErrs = append(expectedErrs, tt.errMsg)
		}

		opts := tt.opts
		got, err := getMethodSpec(&opts)
		if len(expectedErrs) == 0 {
			assert.NoError(t, err, "%v", tt.desc)
			assert.NotNil(t, got, "%v should have result", tt.desc)
			if tt.checkSpec != nil {
				tt.checkSpec(got)
			}
			checkMethod := opts.MethodName
			if tt.overrideMethod != "" {
				checkMethod = tt.overrideMethod
			}
			assert.Equal(t, checkMethod, opts.MethodName, "invalid MethodName")
			continue
		}

		if assert.Error(t, err, "%v", tt.desc) {
			assert.Nil(t, got, "%v should not have result", tt.desc)

			for _, msg := range expectedErrs {
				assert.Contains(t, err.Error(), msg, "%v: invalid error message", tt.desc)
			}
		}
	}
}

func TestRunWithOptions(t *testing.T) {
	validRequestOpts := RequestOptions{
		ThriftFile: validThrift,
		MethodName: fooMethod,
	}

	closedHP := testutils.GetClosedHostPort(t)
	tests := []struct {
		desc   string
		opts   Options
		errMsg string
		want   string
	}{
		{
			desc:   "No thrift file, fail to get method spec",
			errMsg: "while parsing input",
		},
		{
			desc: "No service name, fail to get transport",
			opts: Options{
				ROpts: validRequestOpts,
			},
			errMsg: "while parsing options",
		},
		{
			desc: "Request has invalid field, fail to get request",
			opts: Options{
				ROpts: RequestOptions{
					ThriftFile:  validThrift,
					MethodName:  fooMethod,
					RequestJSON: `{"f1": 1}`,
				},
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{"1.1.1.1:1"},
				},
			},
			errMsg: "while parsing request input",
		},
		{
			desc: "Invalid host:port, fail to make request",
			opts: Options{
				ROpts: validRequestOpts,
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{closedHP},
				},
			},
			errMsg: "Failed while making call",
		},
		{
			desc: "Fail to convert response, bar is non-void",
			opts: Options{
				ROpts: validRequestOpts,
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{echoServer(t, fooMethod, []byte{1, 1})},
				},
			},
			errMsg: "Failed while parsing response",
		},
		{
			desc: "Success",
			opts: Options{
				ROpts: validRequestOpts,
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{echoServer(t, fooMethod, nil)},
				},
			},
			want: "{}",
		},
	}

	var errBuf bytes.Buffer
	var outBuf bytes.Buffer
	out := testOutput{
		fatalf: func(format string, args ...interface{}) {
			errBuf.WriteString(fmt.Sprintf(format, args...))
		},
		printf: func(format string, args ...interface{}) {
			outBuf.WriteString(fmt.Sprintf(format, args...))
		},
	}

	for _, tt := range tests {
		errBuf.Reset()
		outBuf.Reset()

		runComplete := make(chan struct{})
		// runWithOptions expects Fatalf to kill the process, so we run it in a
		// new goroutine and testoutput.Fatalf will only exit the goroutine.
		go func() {
			defer close(runComplete)
			runWithOptions(tt.opts, out)
		}()

		<-runComplete

		if tt.errMsg != "" {
			assert.Empty(t, outBuf.String(), "%v: should have no output", tt.desc)
			assert.Contains(t, errBuf.String(), tt.errMsg, "%v: Invalid error", tt.desc)
			continue
		}

		assert.Empty(t, errBuf.String(), "%v: should not error", tt.desc)
		assert.Contains(t, outBuf.String(), tt.want, "%v: expected output", tt.desc)
	}
}

func TestMain(t *testing.T) {
	origArgs := os.Args
	defer func() { os.Args = origArgs }()

	echoAddr := echoServer(t, fooMethod, nil)
	os.Args = []string{
		"yab",
		"-t", validThrift,
		"foo", fooMethod,
		"-p", echoAddr,
	}

	main()
}
<commit_msg>Add integration test for --health<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"bytes"
	"fmt"
	"os"
	"testing"

	"github.com/thriftrw/thriftrw-go/compile"

	"github.com/stretchr/testify/assert"
	"github.com/uber/tchannel-go/testutils"
	"github.com/uber/tchannel-go/thrift"
)

func TestGetRequest(t *testing.T) {
	method := mustParseFile(t, validThrift).Services["Simple"].Functions["foo"]

	tests := []struct {
		desc   string
		opts   RequestOptions
		errMsg string
	}{
		{
			desc:   "Invalid JSON",
			opts:   RequestOptions{RequestJSON: "{"},
			errMsg: "parse",
		},
		{
			desc:   "Invalid field in request input",
			opts:   RequestOptions{RequestJSON: `{"foo": "1"}`},
			errMsg: "not found",
		},
		{
			desc: "Valid request",
			opts: RequestOptions{},
		},
	}

	for _, tt := range tests {
		got, err := getRequest(tt.opts, method)
		if tt.errMsg == "" {
			assert.NoError(t, err, "%v", tt.desc)
			assert.NotNil(t, got, "%v: Invalid request")
			continue
		}

		if assert.Error(t, err, "%v", tt.desc) {
			assert.Nil(t, got, "%v: Error cases should not return any bytes", tt.desc)
			assert.Contains(t, err.Error(), tt.errMsg, "%v: invalid error", tt.desc)
		}
	}
}

func TestGetMethodSpec(t *testing.T) {
	tests := []struct {
		desc           string
		opts           RequestOptions
		overrideMethod string
		checkSpec      func(*compile.FunctionSpec)
		errMsg         string
		errMsgs        []string
	}{
		{
			desc:   "No thrift file specified",
			opts:   RequestOptions{},
			errMsg: "specify a Thrift file",
		},
		{
			desc:   "Thrift file can't be found",
			opts:   RequestOptions{ThriftFile: "/fake/file"},
			errMsg: "cannot find Thrift file",
		},
		{
			desc:    "Thrift file can't be parsed",
			opts:    RequestOptions{ThriftFile: "testdata/invalid.json"},
			errMsgs: []string{"could not parse Thrift", "parse error"},
		},
		{
			desc:   "Invalid Thrift method name",
			opts:   RequestOptions{ThriftFile: validThrift, MethodName: "A::B::C"},
			errMsg: "invalid Thrift method",
		},
		{
			desc:   "Invalid service name",
			opts:   RequestOptions{ThriftFile: validThrift, MethodName: "UnknownSvc::foo"},
			errMsg: "could not find service",
		},
		{
			desc:   "Invalid method name",
			opts:   RequestOptions{ThriftFile: validThrift, MethodName: "Simple::unknownMethod"},
			errMsg: "could not find method",
		},
		{
			desc:   "Health and method name",
			opts:   RequestOptions{Health: true, MethodName: "And::Another"},
			errMsg: errHealthAndMethod.Error(),
		},
		{
			desc:           "Health method",
			opts:           RequestOptions{Health: true},
			overrideMethod: "Meta::health",
			checkSpec: func(spec *compile.FunctionSpec) {
				assert.Equal(t, 0, len(spec.ArgsSpec), "health method should not have arguments")
				assert.NotNil(t, spec.ResultSpec.ReturnType, "health method should have a return")
			},
		},
		{
			desc: "Valid Thrift file and method name",
			opts: RequestOptions{ThriftFile: validThrift, MethodName: fooMethod},
		},
	}

	for _, tt := range tests {
		expectedErrs := append([]string(nil), tt.errMsgs...)
		if tt.errMsg != "" {
			expectedErrs = append(expectedErrs, tt.errMsg)
		}

		opts := tt.opts
		got, err := getMethodSpec(&opts)
		if len(expectedErrs) == 0 {
			assert.NoError(t, err, "%v", tt.desc)
			assert.NotNil(t, got, "%v should have result", tt.desc)
			if tt.checkSpec != nil {
				tt.checkSpec(got)
			}
			checkMethod := opts.MethodName
			if tt.overrideMethod != "" {
				checkMethod = tt.overrideMethod
			}
			assert.Equal(t, checkMethod, opts.MethodName, "invalid MethodName")
			continue
		}

		if assert.Error(t, err, "%v", tt.desc) {
			assert.Nil(t, got, "%v should not have result", tt.desc)

			for _, msg := range expectedErrs {
				assert.Contains(t, err.Error(), msg, "%v: invalid error message", tt.desc)
			}
		}
	}
}

func TestRunWithOptions(t *testing.T) {
	validRequestOpts := RequestOptions{
		ThriftFile: validThrift,
		MethodName: fooMethod,
	}

	closedHP := testutils.GetClosedHostPort(t)
	tests := []struct {
		desc   string
		opts   Options
		errMsg string
		want   string
	}{
		{
			desc:   "No thrift file, fail to get method spec",
			errMsg: "while parsing input",
		},
		{
			desc: "No service name, fail to get transport",
			opts: Options{
				ROpts: validRequestOpts,
			},
			errMsg: "while parsing options",
		},
		{
			desc: "Request has invalid field, fail to get request",
			opts: Options{
				ROpts: RequestOptions{
					ThriftFile:  validThrift,
					MethodName:  fooMethod,
					RequestJSON: `{"f1": 1}`,
				},
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{"1.1.1.1:1"},
				},
			},
			errMsg: "while parsing request input",
		},
		{
			desc: "Invalid host:port, fail to make request",
			opts: Options{
				ROpts: validRequestOpts,
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{closedHP},
				},
			},
			errMsg: "Failed while making call",
		},
		{
			desc: "Fail to convert response, bar is non-void",
			opts: Options{
				ROpts: validRequestOpts,
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{echoServer(t, fooMethod, []byte{1, 1})},
				},
			},
			errMsg: "Failed while parsing response",
		},
		{
			desc: "Success",
			opts: Options{
				ROpts: validRequestOpts,
				TOpts: TransportOptions{
					ServiceName: "foo",
					HostPorts:   []string{echoServer(t, fooMethod, nil)},
				},
			},
			want: "{}",
		},
	}

	var errBuf bytes.Buffer
	var outBuf bytes.Buffer
	out := testOutput{
		fatalf: func(format string, args ...interface{}) {
			errBuf.WriteString(fmt.Sprintf(format, args...))
		},
		printf: func(format string, args ...interface{}) {
			outBuf.WriteString(fmt.Sprintf(format, args...))
		},
	}

	for _, tt := range tests {
		errBuf.Reset()
		outBuf.Reset()

		runComplete := make(chan struct{})
		// runWithOptions expects Fatalf to kill the process, so we run it in a
		// new goroutine and testoutput.Fatalf will only exit the goroutine.
		go func() {
			defer close(runComplete)
			runWithOptions(tt.opts, out)
		}()

		<-runComplete

		if tt.errMsg != "" {
			assert.Empty(t, outBuf.String(), "%v: should have no output", tt.desc)
			assert.Contains(t, errBuf.String(), tt.errMsg, "%v: Invalid error", tt.desc)
			continue
		}

		assert.Empty(t, errBuf.String(), "%v: should not error", tt.desc)
		assert.Contains(t, outBuf.String(), tt.want, "%v: expected output", tt.desc)
	}
}

func TestMain(t *testing.T) {
	origArgs := os.Args
	defer func() { os.Args = origArgs }()

	echoAddr := echoServer(t, fooMethod, nil)
	os.Args = []string{
		"yab",
		"-t", validThrift,
		"foo", fooMethod,
		"-p", echoAddr,
	}

	main()
}

func TestHealthIntegration(t *testing.T) {
	origArgs := os.Args
	defer func() { os.Args = origArgs }()

	// Create a server with the Meta::health endpoint.
	server := newServer(t)
	thrift.NewServer(server.ch)
	defer server.shutdown()

	os.Args = []string{
		"yab",
		"foo",
		"-p", server.hostPort(),
		"--health",
	}

	main()
}
<|endoftext|>


<commit_before>package thumbnailer

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var samples = []string{
	"no_cover.mp4",
	"no_sound.mkv",
	"no_sound.ogg",
	"sample.gif",
	"sample.psd",
	"with_sound.avi",
	"no_cover.flac",
	"no_cover.ogg",
	"no_sound.mov",
	"no_sound.webm",
	"sample.jpg",
	"sample.tiff",
	"with_cover.mp3",
	"with_sound.mkv",
	"with_sound.ogg",
	"no_sound.avi",
	"no_sound.mp4",
	"no_sound.wmv",
	"sample.pdf",
	"sample.webp",
	"with_sound.mov",
	"with_sound.webm",
	"no_cover.mp3",
	"no_magic.mp3", // No magic numbers
	"no_sound.flv",
	"sample.bmp",
	"sample.png",
	"with_cover.flac",
	"with_sound.mp4",
	"odd_dimensions.webm", // Unconventional dims for a YUV stream
	"alpha.webm",
	"start_black.webm", // Check the histogram thumbnailing
	"exif_orientation.jpg",
	"rare_brand.mp4",
}

func TestProcess(t *testing.T) {
	t.Parallel()

	opts := Options{
		JPEGQuality: 90,
		ThumbDims:   Dims{150, 150},
	}

	for i := range samples {
		sample := samples[i]
		t.Run(sample, func(t *testing.T) {
			t.Parallel()

			f := openSample(t, sample)
			defer f.Close()

			src, thumb, err := Process(f, opts)
			if err != nil && err != ErrNoCoverArt {
				t.Fatal(err)
			}

			if err != ErrNoCoverArt {
				var ext string
				if thumb.IsPNG {
					ext = "png"
				} else {
					ext = "jpg"
				}
				name := fmt.Sprintf(`%s_thumb.%s`, sample, ext)
				writeSample(t, name, thumb.Data)
			}

			src.Data = nil
			thumb.Data = nil
			t.Logf("src:   %v\n", src)
			t.Logf("thumb: %v\n", thumb)
		})
	}
}

func openSample(t *testing.T, name string) *os.File {
	f, err := os.Open(filepath.Join("testdata", name))
	if err != nil {
		t.Fatal(err)
	}
	return f
}

func writeSample(t *testing.T, name string, buf []byte) {
	path := filepath.Join("testdata", name)

	// Remove previous file, if any
	_, err := os.Stat(path)
	switch {
	case os.IsExist(err):
		if err := os.Remove(path); err != nil {
			t.Fatal(err)
		}
	case os.IsNotExist(err):
	case err == nil:
	default:
		t.Fatal(err)
	}

	err = ioutil.WriteFile(path, buf, 0600)
	if err != nil {
		t.Fatal(err)
	}
}

func TestErrorPassing(t *testing.T) {
	t.Parallel()

	f := openSample(t, "sample.txt")
	defer f.Close()

	_, _, err := Process(f, Options{
		ThumbDims: Dims{
			Width:  150,
			Height: 150,
		},
	})
	if err == nil {
		t.Fatal(`expected error`)
	}
}

func TestDimensionValidation(t *testing.T) {
	t.Parallel()

	cases := [...]struct {
		name, file string
		maxW, maxH uint
		err        error
	}{
		{
			name: "width check disabled",
			file: "too wide.jpg",
		},
		{
			name: "too wide",
			file: "too wide.jpg",
			maxW: 2000,
			err:  ErrTooWide,
		},
		{
			name: "height check disabled",
			file: "too tall.jpg",
		},
		{
			name: "too tall",
			file: "too tall.jpg",
			maxH: 2000,
			err:  ErrTooTall,
		},
		{
			name: "pdf pass through",
			file: "sample.pdf",
			maxH: 1,
			maxW: 1,
		},
	}

	for i := range cases {
		c := cases[i]
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()

			opts := Options{
				ThumbDims: Dims{
					Width:  150,
					Height: 150,
				},
				MaxSourceDims: Dims{
					Width:  c.maxW,
					Height: c.maxH,
				},
				JPEGQuality: 90,
			}

			f := openSample(t, c.file)
			defer f.Close()

			_, _, err := Process(f, opts)
			if err != c.err {
				t.Fatalf("unexpected error: `%s` : `%s`", c.err, err)
			}
		})
	}
}

func TestSourceAlreadyThumbSize(t *testing.T) {
	t.Parallel()

	f := openSample(t, "too small.png")
	defer f.Close()

	_, thumb, err := Process(f, Options{
		ThumbDims: Dims{
			Width:  150,
			Height: 150,
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if thumb.Width != 121 {
		t.Errorf("unexpected width: 121 : %d", thumb.Width)
	}
	if thumb.Height != 150 {
		t.Errorf("unexpected height: 150: %d", thumb.Height)
	}
}

func TestMetadataExtraction(t *testing.T) {
	t.Parallel()

	f := openSample(t, "title.mp3")
	defer f.Close()

	src, _, err := Process(f, Options{})
	if err != nil && err != ErrNoCoverArt {
		t.Fatal(err)
	}
	if src.Artist != "Test Artist" {
		t.Errorf("unexpected artist: Test Artist : %s", src.Artist)
	}
	if src.Title != "Test Title" {
		t.Errorf("unexpected title: Test Title: %s", src.Title)
	}
}

func TestWebmAlpha(t *testing.T) {
	t.Parallel()

	f := openSample(t, "alpha.webm")
	defer f.Close()

	_, thumb, err := Process(f, Options{
		ThumbDims: Dims{
			Width:  150,
			Height: 150,
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if !thumb.IsPNG {
		t.Errorf("should contain alpha channel")
	}
}

// Called on `go test -args all`
func TestPanic(t *testing.T) {
	if len(os.Args) != 2 || os.Args[1] != "all" {
		t.Skip("Skipping panic test because it's not fixed yet")
	}

	type B struct {
		c int
	}
	type A struct {
		b *B
	}

	defer func() {
		if r := recover(); r == nil {
			t.Errorf("The code did not panic")
		}
	}()

	a := A{nil}
	fmt.Println(a.b.c)
}
<commit_msg>Mark helper test functions<commit_after>package thumbnailer

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var samples = []string{
	"no_cover.mp4",
	"no_sound.mkv",
	"no_sound.ogg",
	"sample.gif",
	"sample.psd",
	"with_sound.avi",
	"no_cover.flac",
	"no_cover.ogg",
	"no_sound.mov",
	"no_sound.webm",
	"sample.jpg",
	"sample.tiff",
	"with_cover.mp3",
	"with_sound.mkv",
	"with_sound.ogg",
	"no_sound.avi",
	"no_sound.mp4",
	"no_sound.wmv",
	"sample.pdf",
	"sample.webp",
	"with_sound.mov",
	"with_sound.webm",
	"no_cover.mp3",
	"no_magic.mp3", // No magic numbers
	"no_sound.flv",
	"sample.bmp",
	"sample.png",
	"with_cover.flac",
	"with_sound.mp4",
	"odd_dimensions.webm", // Unconventional dims for a YUV stream
	"alpha.webm",
	"start_black.webm", // Check the histogram thumbnailing
	"exif_orientation.jpg",
	"rare_brand.mp4",
}

func TestProcess(t *testing.T) {
	t.Parallel()

	opts := Options{
		JPEGQuality: 90,
		ThumbDims:   Dims{150, 150},
	}

	for i := range samples {
		sample := samples[i]
		t.Run(sample, func(t *testing.T) {
			t.Parallel()

			f := openSample(t, sample)
			defer f.Close()

			src, thumb, err := Process(f, opts)
			if err != nil && err != ErrNoCoverArt {
				t.Fatal(err)
			}

			if err != ErrNoCoverArt {
				var ext string
				if thumb.IsPNG {
					ext = "png"
				} else {
					ext = "jpg"
				}
				name := fmt.Sprintf(`%s_thumb.%s`, sample, ext)
				writeSample(t, name, thumb.Data)
			}

			src.Data = nil
			thumb.Data = nil
			t.Logf("src:   %v\n", src)
			t.Logf("thumb: %v\n", thumb)
		})
	}
}

func openSample(t *testing.T, name string) *os.File {
	t.Helper()

	f, err := os.Open(filepath.Join("testdata", name))
	if err != nil {
		t.Fatal(err)
	}
	return f
}

func writeSample(t *testing.T, name string, buf []byte) {
	t.Helper()
	path := filepath.Join("testdata", name)

	// Remove previous file, if any
	_, err := os.Stat(path)
	switch {
	case os.IsExist(err):
		if err := os.Remove(path); err != nil {
			t.Fatal(err)
		}
	case os.IsNotExist(err):
	case err == nil:
	default:
		t.Fatal(err)
	}

	err = ioutil.WriteFile(path, buf, 0600)
	if err != nil {
		t.Fatal(err)
	}
}

func TestErrorPassing(t *testing.T) {
	t.Parallel()

	f := openSample(t, "sample.txt")
	defer f.Close()

	_, _, err := Process(f, Options{
		ThumbDims: Dims{
			Width:  150,
			Height: 150,
		},
	})
	if err == nil {
		t.Fatal(`expected error`)
	}
}

func TestDimensionValidation(t *testing.T) {
	t.Parallel()

	cases := [...]struct {
		name, file string
		maxW, maxH uint
		err        error
	}{
		{
			name: "width check disabled",
			file: "too wide.jpg",
		},
		{
			name: "too wide",
			file: "too wide.jpg",
			maxW: 2000,
			err:  ErrTooWide,
		},
		{
			name: "height check disabled",
			file: "too tall.jpg",
		},
		{
			name: "too tall",
			file: "too tall.jpg",
			maxH: 2000,
			err:  ErrTooTall,
		},
		{
			name: "pdf pass through",
			file: "sample.pdf",
			maxH: 1,
			maxW: 1,
		},
	}

	for i := range cases {
		c := cases[i]
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()

			opts := Options{
				ThumbDims: Dims{
					Width:  150,
					Height: 150,
				},
				MaxSourceDims: Dims{
					Width:  c.maxW,
					Height: c.maxH,
				},
				JPEGQuality: 90,
			}

			f := openSample(t, c.file)
			defer f.Close()

			_, _, err := Process(f, opts)
			if err != c.err {
				t.Fatalf("unexpected error: `%s` : `%s`", c.err, err)
			}
		})
	}
}

func TestSourceAlreadyThumbSize(t *testing.T) {
	t.Parallel()

	f := openSample(t, "too small.png")
	defer f.Close()

	_, thumb, err := Process(f, Options{
		ThumbDims: Dims{
			Width:  150,
			Height: 150,
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if thumb.Width != 121 {
		t.Errorf("unexpected width: 121 : %d", thumb.Width)
	}
	if thumb.Height != 150 {
		t.Errorf("unexpected height: 150: %d", thumb.Height)
	}
}

func TestMetadataExtraction(t *testing.T) {
	t.Parallel()

	f := openSample(t, "title.mp3")
	defer f.Close()

	src, _, err := Process(f, Options{})
	if err != nil && err != ErrNoCoverArt {
		t.Fatal(err)
	}
	if src.Artist != "Test Artist" {
		t.Errorf("unexpected artist: Test Artist : %s", src.Artist)
	}
	if src.Title != "Test Title" {
		t.Errorf("unexpected title: Test Title: %s", src.Title)
	}
}

func TestWebmAlpha(t *testing.T) {
	t.Parallel()

	f := openSample(t, "alpha.webm")
	defer f.Close()

	_, thumb, err := Process(f, Options{
		ThumbDims: Dims{
			Width:  150,
			Height: 150,
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if !thumb.IsPNG {
		t.Errorf("should contain alpha channel")
	}
}

// Called on `go test -args all`
func TestPanic(t *testing.T) {
	if len(os.Args) != 2 || os.Args[1] != "all" {
		t.Skip("Skipping panic test because it's not fixed yet")
	}

	type B struct {
		c int
	}
	type A struct {
		b *B
	}

	defer func() {
		if r := recover(); r == nil {
			t.Errorf("The code did not panic")
		}
	}()

	a := A{nil}
	fmt.Println(a.b.c)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"fmt"
	"testing"
)

type wantErr string

func tokErr(msg string) wantErr {
	return wantErr("cannot tokenize expr: "+msg)
}

func parseErr(msg string) wantErr {
	return wantErr("cannot parse expr: "+msg)
}

func TestGrep(t *testing.T) {
	tests := []struct {
		expr, src string
		anyWant   interface{}
	}{
		// expr tokenize errors
		{"$", "", tokErr("1:2: $ must be followed by ident, got EOF")},
		{`"`, "", tokErr("1:1: string literal not terminated")},

		// expr parse errors
		{"{", "", parseErr("6:2: expected '}', found 'EOF'")},

		// basic lits
		{"123", "123", true},
		{"false", "true", false},

		// wildcards
		{"$x", "rune", true},
		{"foo($x, $x)", "foo(1, 2)", false},
		{"foo($_, $_)", "foo(1, 2)", true},
		{"foo($x, $y, $y)", "foo(1, 2, 2)", true},

		// many expressions
		{"$x, $y", "1, 2", true},
		{"$x, $y", "1", false},

		// composite lits
		{"[]float64{$x}", "[]float64{3}", true},
		{"[2]bool{$x, 0}", "[2]bool{3, 1}", false},
		{"someStruct{fld: $x}", "someStruct{fld: a, fld2: b}", false},
		{"map[int]int{1: $x}", "map[int]int{1: a}", true},

		// func lits
		{"func($s string) { print($s) }", "func(a string) { print(a) }", true},
		{"func($x ...$t) {}", "func(a ...int) {}", true},

		// more types
		{"struct{field $t}", "struct{field int}", true},
		{"interface{$x() int}", "interface{i() int}", true},
		{"chan $x", "chan bool", true},
		{"<-chan $x", "chan bool", false},
		{"chan $x", "chan<- bool", false},

		// many types
		{"chan $x, interface{}", "chan int, interface{}", true},
		{"chan $x, interface{}", "chan int", false},

		// parens
		{"($x)", "(a + b)", true},
		{"($x)", "a + b", false},

		// unary ops
		{"-someConst", "- someConst", true},
		{"*someVar", "* someVar", true},

		// binary ops
		{"$x == $y", "a == b", true},
		{"$x == $y", "123", false},
		{"$x == $y", "a != b", false},
		{"$x - $x", "a - b", false},

		// calls
		{"someFunc($x)", "someFunc(a > b)", true},

		// selector
		{"$x.Field", "a.Field", true},
		{"$x.Field", "a.field", false},
		{"$x.Method()", "a.Method()", true},

		// index
		{"$x[len($x)-1]", "a[len(a)-1]", true},
		{"$x[len($x)-1]", "a[len(b)-1]", false},

		// slicing
		{"$x[:$y]", "a[:1]", true},
		{"$x[3:]", "a[3:5:5]", false},

		// type asserts
		{"$x.(string)", "a.(string)", true},

		// elipsis
		{"append($x, $y...)", "append(a, bs...)", true},
		{"foo($x...)", "foo(a)", false},
		{"foo($x...)", "foo(a, b)", false},

		// many statements
		{"$x(); $y()", "a(); b(); c()", true},
		{"$x(); $y()", "a()", false},

		// block
		{"{ $x }", "{ a() }", true},
		{"{ $x }", "{ a(); b() }", false},
	}
	for i, tc := range tests {
		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
			grepTest(t, tc.expr, tc.src, tc.anyWant)
		})
	}
}

func grepTest(t *testing.T, expr, src string, anyWant interface{}) {
	terr := func(format string, a ...interface{}) {
		t.Errorf("%s | %s: %s", expr, src, fmt.Sprintf(format, a...))
	}
	match, err := grep(expr, src)
	switch want := anyWant.(type) {
	case wantErr:
		if err == nil {
			terr("wanted error %q, got none", want)
		} else if got := err.Error(); got != string(want) {
			terr("wanted error %q, got %q", got, want)
		}
	case bool:
		if err != nil {
			terr("unexpected error: %v", err)
			return
		}
		if match && !want {
			terr("got unexpected match")
		} else if !match && want {
			terr("wanted match, got none")
		}
	}
}
<commit_msg>Adapt the tests to any number of matches<commit_after>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"fmt"
	"testing"
)

type wantErr string

func tokErr(msg string) wantErr {
	return wantErr("cannot tokenize expr: " + msg)
}

func parseErr(msg string) wantErr {
	return wantErr("cannot parse expr: " + msg)
}

type matches uint

var noMatch = matches(0)

func TestGrep(t *testing.T) {
	tests := []struct {
		expr, src string
		anyWant   interface{}
	}{
		// expr tokenize errors
		{"$", "", tokErr("1:2: $ must be followed by ident, got EOF")},
		{`"`, "", tokErr("1:1: string literal not terminated")},

		// expr parse errors
		{"{", "", parseErr("6:2: expected '}', found 'EOF'")},

		// basic lits
		{"123", "123", matches(1)},
		{"false", "true", noMatch},

		// wildcards
		{"$x", "rune", matches(1)},
		{"foo($x, $x)", "foo(1, 2)", noMatch},
		{"foo($_, $_)", "foo(1, 2)", matches(1)},
		{"foo($x, $y, $y)", "foo(1, 2, 2)", matches(1)},

		// many expressions
		{"$x, $y", "1, 2", matches(1)},
		{"$x, $y", "1", noMatch},

		// composite lits
		{"[]float64{$x}", "[]float64{3}", matches(1)},
		{"[2]bool{$x, 0}", "[2]bool{3, 1}", noMatch},
		{"someStruct{fld: $x}", "someStruct{fld: a, fld2: b}", noMatch},
		{"map[int]int{1: $x}", "map[int]int{1: a}", matches(1)},

		// func lits
		{"func($s string) { print($s) }", "func(a string) { print(a) }", matches(1)},
		{"func($x ...$t) {}", "func(a ...int) {}", matches(1)},

		// more types
		{"struct{field $t}", "struct{field int}", matches(1)},
		{"interface{$x() int}", "interface{i() int}", matches(1)},
		{"chan $x", "chan bool", matches(1)},
		{"<-chan $x", "chan bool", noMatch},
		{"chan $x", "chan<- bool", noMatch},

		// many types
		{"chan $x, interface{}", "chan int, interface{}", matches(1)},
		{"chan $x, interface{}", "chan int", noMatch},

		// parens
		{"($x)", "(a + b)", matches(1)},
		{"($x)", "a + b", noMatch},

		// unary ops
		{"-someConst", "- someConst", matches(1)},
		{"*someVar", "* someVar", matches(1)},

		// binary ops
		{"$x == $y", "a == b", matches(1)},
		{"$x == $y", "123", noMatch},
		{"$x == $y", "a != b", noMatch},
		{"$x - $x", "a - b", noMatch},

		// calls
		{"someFunc($x)", "someFunc(a > b)", matches(1)},

		// selector
		{"$x.Field", "a.Field", matches(1)},
		{"$x.Field", "a.field", noMatch},
		{"$x.Method()", "a.Method()", matches(1)},

		// index
		{"$x[len($x)-1]", "a[len(a)-1]", matches(1)},
		{"$x[len($x)-1]", "a[len(b)-1]", noMatch},

		// slicing
		{"$x[:$y]", "a[:1]", matches(1)},
		{"$x[3:]", "a[3:5:5]", noMatch},

		// type asserts
		{"$x.(string)", "a.(string)", matches(1)},

		// elipsis
		{"append($x, $y...)", "append(a, bs...)", matches(1)},
		{"foo($x...)", "foo(a)", noMatch},
		{"foo($x...)", "foo(a, b)", noMatch},

		// many statements
		{"$x(); $y()", "a(); b(); c()", matches(1)},
		{"$x(); $y()", "a()", noMatch},

		// block
		{"{ $x }", "{ a() }", matches(1)},
		{"{ $x }", "{ a(); b() }", noMatch},
	}
	for i, tc := range tests {
		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
			grepTest(t, tc.expr, tc.src, tc.anyWant)
		})
	}
}

func grepTest(t *testing.T, expr, src string, anyWant interface{}) {
	terr := func(format string, a ...interface{}) {
		t.Errorf("%s | %s: %s", expr, src, fmt.Sprintf(format, a...))
	}
	match, err := grep(expr, src)
	switch want := anyWant.(type) {
	case wantErr:
		if err == nil {
			terr("wanted error %q, got none", want)
		} else if got := err.Error(); got != string(want) {
			terr("wanted error %q, got %q", got, want)
		}
	case matches:
		if err != nil {
			terr("unexpected error: %v", err)
			return
		}
		if match && want == 0 {
			terr("got unexpected match")
		} else if !match && want > 0 {
			terr("wanted match, got none")
		}
	default:
		panic(fmt.Sprintf("unexpected anyWant type: %T", anyWant))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/ian-kent/MailHog/mailhog"
	"github.com/ian-kent/MailHog/mailhog/storage"
	"github.com/stretchr/testify/assert"
	"net"
	"regexp"
	"strings"
	"testing"
)

// FIXME requires a running instance of MailHog

func TestBasicHappyPath(t *testing.T) {
	buf := make([]byte, 1024)

	// Open a connection
	conn, err := net.Dial("tcp", "127.0.0.1:1025")
	assert.Nil(t, err)

	// Read the greeting
	n, err := conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "220 mailhog.example ESMTP Go-MailHog\n")

	// Send EHLO
	_, err = conn.Write([]byte("EHLO localhost\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "250 Hello localhost\n")

	// Send MAIL
	_, err = conn.Write([]byte("MAIL From:<nobody@mailhog.example>\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "250 Sender nobody@mailhog.example ok\n")

	// Send RCPT
	_, err = conn.Write([]byte("RCPT To:<someone@mailhog.example>\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "250 Recipient someone@mailhog.example ok\n")

	// Send DATA
	_, err = conn.Write([]byte("DATA\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "354 End data with <CR><LF>.<CR><LF>\n")

	// Send the message
	content := "Content-Type: text/plain\r\n"
	content += "Content-Length: 220\r\n"
	content += "From: Nobody <nobody@mailhog.example>\r\n"
	content += "To: Someone <someone@mailhog.example>\r\n"
	content += "\r\n"
	content += "Hi there :)\r\n"
	content += ".\r\n"
	_, err = conn.Write([]byte(content))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	r, _ := regexp.Compile("250 Ok: queued as ([0-9a-f]+)\n")
	match := r.FindStringSubmatch(string(buf[0:n]))
	assert.NotNil(t, match)

	// Send QUIT
	_, err = conn.Write([]byte("QUIT\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "221 Bye\n")

	message, err := storage.Load(mailhog.DefaultConfig(), match[1])
	assert.Nil(t, err)
	assert.NotNil(t, message)

	assert.Equal(t, message.From.Domain, "mailhog.example", "sender domain is mailhog.example")
	assert.Equal(t, message.From.Mailbox, "nobody", "sender mailbox is nobody")
	assert.Equal(t, message.From.Params, "", "sender params is empty")
	assert.Equal(t, len(message.From.Relays), 0, "sender has no relays")

	assert.Equal(t, len(message.To), 1, "message has 1 recipient")

	assert.Equal(t, message.To[0].Domain, "mailhog.example", "recipient domain is mailhog.example")
	assert.Equal(t, message.To[0].Mailbox, "someone", "recipient mailbox is someone")
	assert.Equal(t, message.To[0].Params, "", "recipient params is empty")
	assert.Equal(t, len(message.To[0].Relays), 0, "recipient has no relays")

	assert.Equal(t, len(message.Content.Headers), 7, "message has 7 headers")
	assert.Equal(t, message.Content.Headers["Content-Type"], []string{"text/plain"}, "Content-Type header is text/plain")
	assert.Equal(t, message.Content.Headers["Content-Length"], []string{"220"}, "Content-Length is 220")
	assert.Equal(t, message.Content.Headers["To"], []string{"Someone <someone@mailhog.example>"}, "To is Someone <someone@mailhog.example>")
	assert.Equal(t, message.Content.Headers["From"], []string{"Nobody <nobody@mailhog.example>"}, "From is Nobody <nobody@mailhog.example>")
	assert.True(t, strings.HasPrefix(message.Content.Headers["Received"][0], "from localhost by mailhog.example (Go-MailHog)\r\n          id "+match[1]+"@mailhog.example; "), "Received header is correct")
	assert.Equal(t, message.Content.Headers["Return-Path"], []string{"<nobody@mailhog.example>"}, "Return-Path is <nobody@mailhog.example>")
	assert.Equal(t, message.Content.Headers["Message-ID"], []string{match[1] + "@mailhog.example"}, "Message-ID is "+match[1]+"@mailhog.example")

	assert.Equal(t, message.Content.Body, "Hi there :)", "message has correct body")
}
<commit_msg>Add subject to example test<commit_after>package main

import (
	"github.com/ian-kent/MailHog/mailhog"
	"github.com/ian-kent/MailHog/mailhog/storage"
	"github.com/stretchr/testify/assert"
	"net"
	"regexp"
	"strings"
	"testing"
)

// FIXME requires a running instance of MailHog

func TestBasicHappyPath(t *testing.T) {
	buf := make([]byte, 1024)

	// Open a connection
	conn, err := net.Dial("tcp", "127.0.0.1:1025")
	assert.Nil(t, err)

	// Read the greeting
	n, err := conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "220 mailhog.example ESMTP Go-MailHog\n")

	// Send EHLO
	_, err = conn.Write([]byte("EHLO localhost\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "250 Hello localhost\n")

	// Send MAIL
	_, err = conn.Write([]byte("MAIL From:<nobody@mailhog.example>\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "250 Sender nobody@mailhog.example ok\n")

	// Send RCPT
	_, err = conn.Write([]byte("RCPT To:<someone@mailhog.example>\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "250 Recipient someone@mailhog.example ok\n")

	// Send DATA
	_, err = conn.Write([]byte("DATA\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "354 End data with <CR><LF>.<CR><LF>\n")

	// Send the message
	content := "Content-Type: text/plain\r\n"
	content += "Content-Length: 220\r\n"
	content += "From: Nobody <nobody@mailhog.example>\r\n"
	content += "To: Someone <someone@mailhog.example>\r\n"
	content += "Subject: Example message\r\n"
	content += "\r\n"
	content += "Hi there :)\r\n"
	content += ".\r\n"
	_, err = conn.Write([]byte(content))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	r, _ := regexp.Compile("250 Ok: queued as ([0-9a-f]+)\n")
	match := r.FindStringSubmatch(string(buf[0:n]))
	assert.NotNil(t, match)

	// Send QUIT
	_, err = conn.Write([]byte("QUIT\r\n"))
	assert.Nil(t, err)

	// Read the response
	n, err = conn.Read(buf)
	assert.Nil(t, err)
	assert.Equal(t, string(buf[0:n]), "221 Bye\n")

	message, err := storage.Load(mailhog.DefaultConfig(), match[1])
	assert.Nil(t, err)
	assert.NotNil(t, message)

	assert.Equal(t, message.From.Domain, "mailhog.example", "sender domain is mailhog.example")
	assert.Equal(t, message.From.Mailbox, "nobody", "sender mailbox is nobody")
	assert.Equal(t, message.From.Params, "", "sender params is empty")
	assert.Equal(t, len(message.From.Relays), 0, "sender has no relays")

	assert.Equal(t, len(message.To), 1, "message has 1 recipient")

	assert.Equal(t, message.To[0].Domain, "mailhog.example", "recipient domain is mailhog.example")
	assert.Equal(t, message.To[0].Mailbox, "someone", "recipient mailbox is someone")
	assert.Equal(t, message.To[0].Params, "", "recipient params is empty")
	assert.Equal(t, len(message.To[0].Relays), 0, "recipient has no relays")

	assert.Equal(t, len(message.Content.Headers), 8, "message has 7 headers")
	assert.Equal(t, message.Content.Headers["Content-Type"], []string{"text/plain"}, "Content-Type header is text/plain")
	assert.Equal(t, message.Content.Headers["Subject"], []string{"Example message"}, "Subject header is Example message")
	assert.Equal(t, message.Content.Headers["Content-Length"], []string{"220"}, "Content-Length is 220")
	assert.Equal(t, message.Content.Headers["To"], []string{"Someone <someone@mailhog.example>"}, "To is Someone <someone@mailhog.example>")
	assert.Equal(t, message.Content.Headers["From"], []string{"Nobody <nobody@mailhog.example>"}, "From is Nobody <nobody@mailhog.example>")
	assert.True(t, strings.HasPrefix(message.Content.Headers["Received"][0], "from localhost by mailhog.example (Go-MailHog)\r\n          id "+match[1]+"@mailhog.example; "), "Received header is correct")
	assert.Equal(t, message.Content.Headers["Return-Path"], []string{"<nobody@mailhog.example>"}, "Return-Path is <nobody@mailhog.example>")
	assert.Equal(t, message.Content.Headers["Message-ID"], []string{match[1] + "@mailhog.example"}, "Message-ID is "+match[1]+"@mailhog.example")

	assert.Equal(t, message.Content.Body, "Hi there :)", "message has correct body")
}
<|endoftext|>


<commit_before>// +build integration

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"

	"regexp"

	"github.com/elliotchance/c2go/util"
)

type programOut struct {
	stdout bytes.Buffer
	stderr bytes.Buffer
	isZero bool
}

// TestIntegrationScripts tests all programs in the tests directory.
//
// Integration tests are not run by default (only unit tests). These are
// indicated by the build flags at the top of the file. To include integration
// tests use:
//
//     go test -tags=integration
//
// You can also run a single file with:
//
//     go test -tags=integration -run=TestIntegrationScripts/tests/ctype/isalnum.c
//
func TestIntegrationScripts(t *testing.T) {
	testFiles, err := filepath.Glob("tests/*.c")
	if err != nil {
		t.Fatal(err)
	}

	exampleFiles, err := filepath.Glob("examples/*.c")
	if err != nil {
		t.Fatal(err)
	}

	files := append(testFiles, exampleFiles...)

	isVerbose := flag.CommandLine.Lookup("test.v").Value.String() == "true"

	totalTapTests := 0

	buildFolder = "build"
	os.RemoveAll(buildFolder)
	// Create build folder
	os.Mkdir(buildFolder, os.ModePerm)

	t.Parallel()
	var (
		cPath  = buildFolder + os.PathSeparator + "a.out"
		goPath = buildFolder + os.PathSeparator + "go.out"
		stdin  = "7"
		args   = []string{"some", "args"}
	)

	for _, file := range files {
		t.Run(file, func(t *testing.T) {
			cProgram := programOut{}
			goProgram := programOut{}

			// TODO: create sub dir for test

			// Compile C.
			out, err := exec.Command("clang", "-lm", "-o", cPath, file).CombinedOutput()
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			// Run C program
			cmd := exec.Command(cPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &cProgram.stdout
			cmd.Stderr = &cProgram.stderr
			err = cmd.Run()
			cProgram.isZero = err == nil

			programArgs := ProgramArgs{
				inputFile:   file,
				outputFile:  buildFolder + os.PathSeparator + "main.go",
				packageName: "main",
			}

			// Compile Go
			err = Start(programArgs)
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			buildErr, err := exec.Command("go", "build", "-o", goPath, buildFolder+os.PathSeparator+"main.go").CombinedOutput()
			if err != nil {
				t.Fatal(string(buildErr), err)
			}

			// Run Go program
			cmd = exec.Command(goPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &goProgram.stdout
			cmd.Stderr = &goProgram.stderr
			err = cmd.Run()
			goProgram.isZero = err == nil

			// Check for special exit codes that signal that tests have failed.
			if exitError, ok := err.(*exec.ExitError); ok {
				exitStatus := exitError.Sys().(syscall.WaitStatus).ExitStatus()
				if exitStatus == 101 || exitStatus == 102 {
					t.Fatal(goProgram.stdout.String())
				}
			}

			// Check if both exit codes are zero (or non-zero)
			if cProgram.isZero != goProgram.isZero {
				t.Fatalf("Exit statuses did not match.\n" +
					util.ShowDiff(cProgram.stdout.String(),
						goProgram.stdout.String()),
				)
			}

			// Check stderr
			if cProgram.stderr.String() != goProgram.stderr.String() {
				t.Fatalf("Expected %q, Got: %q",
					cProgram.stderr.String(),
					goProgram.stderr.String())
			}

			// Check stdout
			if cProgram.stdout.String() != goProgram.stdout.String() {
				t.Fatalf(util.ShowDiff(cProgram.stdout.String(),
					goProgram.stdout.String()))
			}

			// If this is not an example we will extact the number of tests run.
			if strings.Index(file, "examples/") == -1 && isVerbose {
				firstLine := strings.Split(goProgram.stdout.String(), "\n")[0]

				matches := regexp.MustCompile(`1\.\.(\d+)`).
					FindStringSubmatch(firstLine)
				if len(matches) == 0 {
					t.Fatalf("Test did not output tap: %s", file)
				}

				fmt.Printf("TAP: # %s: %s tests\n", file, matches[1])
				totalTapTests += util.Atoi(matches[1])
			}
		})
	}

	if isVerbose {
		fmt.Printf("TAP: # Total tests: %d\n", totalTapTests)
	}
}

func TestStartPreprocess(t *testing.T) {
	// temp dir
	tempDir := os.TempDir()

	// create temp file with garantee
	// wrong file body
	tempFile, err := newTempFile(tempDir, "c2go", "preprocess.c")
	if err != nil {
		t.Errorf("Cannot create temp file for execute test")
	}
	defer os.Remove(tempFile.Name())

	fmt.Fprintf(tempFile, "#include <AbsoluteWrongInclude.h>\nint main(void){\nwrong();\n}")

	err = tempFile.Close()
	if err != nil {
		t.Errorf("Cannot close the temp file")
	}

	var args ProgramArgs
	args.inputFile = tempFile.Name()

	err = Start(args)
	if err == nil {
		t.Errorf("Cannot test preprocess of application")
	}
}

func TestGoPath(t *testing.T) {
	gopath := "GOPATH"

	existEnv := os.Getenv(gopath)
	if existEnv == "" {
		t.Errorf("$GOPATH is not set")
	}

	// return env.var.
	defer func() {
		err := os.Setenv(gopath, existEnv)
		if err != nil {
			t.Errorf("Cannot restore the value of $GOPATH")
		}
	}()

	// reset value of env.var.
	err := os.Setenv(gopath, "")
	if err != nil {
		t.Errorf("Cannot set value of $GOPATH")
	}

	// testing
	err = Start(ProgramArgs{})
	if err == nil {
		t.Errorf(err.Error())
	}
}
<commit_msg>parallel testing<commit_after>// +build integration

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"

	"regexp"

	"github.com/elliotchance/c2go/util"
)

type programOut struct {
	stdout bytes.Buffer
	stderr bytes.Buffer
	isZero bool
}

// TestIntegrationScripts tests all programs in the tests directory.
//
// Integration tests are not run by default (only unit tests). These are
// indicated by the build flags at the top of the file. To include integration
// tests use:
//
//     go test -tags=integration
//
// You can also run a single file with:
//
//     go test -tags=integration -run=TestIntegrationScripts/tests/ctype/isalnum.c
//
func TestIntegrationScripts(t *testing.T) {
	testFiles, err := filepath.Glob("tests/*.c")
	if err != nil {
		t.Fatal(err)
	}

	exampleFiles, err := filepath.Glob("examples/*.c")
	if err != nil {
		t.Fatal(err)
	}

	files := append(testFiles, exampleFiles...)

	isVerbose := flag.CommandLine.Lookup("test.v").Value.String() == "true"

	totalTapTests := 0

	var (
		buildFolder  = "build"
		cFileName    = "a.out"
		goFileName   = "go.out"
		mainFileName = "main.go"
		stdin        = "7"
		args         = []string{"some", "args"}
		separator    = string(os.PathSeparator)
	)

	// Create build folder
	err = os.MkdirAll(buildFolder, os.ModePerm)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	t.Parallel()

	for _, file := range files {
		t.Run(file, func(t *testing.T) {
			cProgram := programOut{}
			goProgram := programOut{}

			// create subfolders for test
			subFolder := buildFolder + separator + strings.Split(file, ".")[0] + separator
			cPath := subFolder + cFileName
			goPath := subFolder + goFileName

			// Create build folder
			err := os.MkdirAll(subFolder, os.ModePerm)
			if err != nil {
				t.Fatalf("error: %v", err)
			}

			// Compile C.
			out, err := exec.Command("clang", "-lm", "-o", cPath, file).CombinedOutput()
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			// Run C program
			cmd := exec.Command(cPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &cProgram.stdout
			cmd.Stderr = &cProgram.stderr
			err = cmd.Run()
			cProgram.isZero = err == nil

			programArgs := ProgramArgs{
				inputFile:   file,
				outputFile:  subFolder + separator + mainFileName,
				packageName: "main",
			}

			// Compile Go
			err = Start(programArgs)
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			fmt.Println("sdsdsdsdsds")
			{
				buildErr := exec.Command("go", "build", "-o", goPath, subFolder+mainFileName)
				var out bytes.Buffer
				var stderr bytes.Buffer
				buildErr.Stdout = &out
				buildErr.Stderr = &stderr
				err = buildErr.Run()
				if err != nil {
					t.Fatalf("preprocess failed: %v\nStdErr = %v", err, stderr.String())
				}
			}

			// Run Go program
			cmd = exec.Command(goPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &goProgram.stdout
			cmd.Stderr = &goProgram.stderr
			err = cmd.Run()
			goProgram.isZero = err == nil

			// Check for special exit codes that signal that tests have failed.
			if exitError, ok := err.(*exec.ExitError); ok {
				exitStatus := exitError.Sys().(syscall.WaitStatus).ExitStatus()
				if exitStatus == 101 || exitStatus == 102 {
					t.Fatal(goProgram.stdout.String())
				}
			}

			// Check if both exit codes are zero (or non-zero)
			if cProgram.isZero != goProgram.isZero {
				t.Fatalf("Exit statuses did not match.\n" +
					util.ShowDiff(cProgram.stdout.String(),
						goProgram.stdout.String()),
				)
			}

			// Check stderr
			if cProgram.stderr.String() != goProgram.stderr.String() {
				t.Fatalf("Expected %q, Got: %q",
					cProgram.stderr.String(),
					goProgram.stderr.String())
			}

			// Check stdout
			if cProgram.stdout.String() != goProgram.stdout.String() {
				t.Fatalf(util.ShowDiff(cProgram.stdout.String(),
					goProgram.stdout.String()))
			}

			// If this is not an example we will extact the number of tests run.
			if strings.Index(file, "examples/") == -1 && isVerbose {
				firstLine := strings.Split(goProgram.stdout.String(), "\n")[0]

				matches := regexp.MustCompile(`1\.\.(\d+)`).
					FindStringSubmatch(firstLine)
				if len(matches) == 0 {
					t.Fatalf("Test did not output tap: %s", file)
				}

				fmt.Printf("TAP: # %s: %s tests\n", file, matches[1])
				totalTapTests += util.Atoi(matches[1])
			}
		})
	}

	if isVerbose {
		fmt.Printf("TAP: # Total tests: %d\n", totalTapTests)
	}
}

func TestStartPreprocess(t *testing.T) {
	// temp dir
	tempDir := os.TempDir()

	// create temp file with garantee
	// wrong file body
	tempFile, err := newTempFile(tempDir, "c2go", "preprocess.c")
	if err != nil {
		t.Errorf("Cannot create temp file for execute test")
	}
	defer os.Remove(tempFile.Name())

	fmt.Fprintf(tempFile, "#include <AbsoluteWrongInclude.h>\nint main(void){\nwrong();\n}")

	err = tempFile.Close()
	if err != nil {
		t.Errorf("Cannot close the temp file")
	}

	var args ProgramArgs
	args.inputFile = tempFile.Name()

	err = Start(args)
	if err == nil {
		t.Errorf("Cannot test preprocess of application")
	}
}

func TestGoPath(t *testing.T) {
	gopath := "GOPATH"

	existEnv := os.Getenv(gopath)
	if existEnv == "" {
		t.Errorf("$GOPATH is not set")
	}

	// return env.var.
	defer func() {
		err := os.Setenv(gopath, existEnv)
		if err != nil {
			t.Errorf("Cannot restore the value of $GOPATH")
		}
	}()

	// reset value of env.var.
	err := os.Setenv(gopath, "")
	if err != nil {
		t.Errorf("Cannot set value of $GOPATH")
	}

	// testing
	err = Start(ProgramArgs{})
	if err == nil {
		t.Errorf(err.Error())
	}
}
<|endoftext|>


<commit_before>package srcgraph

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/buildstore"

	"testing"

	"github.com/aybabtme/color/brush"
)

var mode = flag.String("test.mode", "test", "[test|keep|gen] 'test' runs test as normal; keep keeps around generated test files for inspection after tests complete; 'gen' generates new expected test data")
var repoMatch = flag.String("test.repo", "", "only test `srcgraph make` for repos that contain this string")

func TestMakeCmd(t *testing.T) {
	// Since we exec `srcgraph`, make sure it's up-to-date.
	if out, err := exec.Command("make", "-C", "..", "srcgraph").CombinedOutput(); err != nil {
		t.Errorf("Failed to build srcgraph for `srcgraph make` tests: %s.\n\nOutput was:\n%s", err, out)
		return
	}

	const testReposDir = "testdata/repos"
	fis, err := ioutil.ReadDir("testdata/repos")
	if err != nil {
		t.Fatal(err)
	}
	n := 0
	for _, fi := range fis {
		if repoDir := fi.Name(); strings.Contains(repoDir, *repoMatch) {
			testMakeCmd(t, filepath.Join(testReposDir, repoDir))
			n++
		}
	}
	if n == 0 {
		t.Errorf("No TestMakeCmd cases were run. Did none match your constraint -test.match=%q?", *repoMatch)
	}
}

func testMakeCmd(t *testing.T, repoDir string) {
	// Update the git submodule automatically.
	cmd := exec.Command("git", "submodule", "update", filepath.Join("srcgraph", repoDir))
	cmd.Dir = ".."
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Errorf("Failed to update git submodule for %s: %s.\n\nOutput was:\n%s", repoDir, err, out)
		return
	}

	repoName := filepath.Base(repoDir)

	// Directories for the actual ("got") and expected ("want") outputs.
	gotOutputDir := filepath.Join(repoDir, "../../repos-output/got", repoName)
	wantOutputDir := filepath.Join(repoDir, "../../repos-output/want", repoName)

	// Determine and wipe the desired output dir.
	var outputDir string
	if *mode == "gen" {
		outputDir = wantOutputDir
	} else {
		outputDir = gotOutputDir
	}
	outputDir, _ = filepath.Abs(outputDir)
	if err := os.RemoveAll(outputDir); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		t.Fatal(err)
	}

	// Symlink ${repoDir}/.sourcegraph-data/${commitID} to the desired output dir.
	origOutputDestDir := filepath.Join(repoDir, buildstore.BuildDataDirName, getHEADOrTipCommitID(t, repoDir))
	if err := os.Remove(origOutputDestDir); err != nil && !os.IsNotExist(err) {
		t.Fatal(err)
	}
	if err := os.Symlink(outputDir, origOutputDestDir); err != nil {
		t.Error(err)
		return
	}
	defer func() {
		// Remove the symlink when we're done so the repo doesn't have
		// uncommitted changes.
		if err := os.Remove(origOutputDestDir); err != nil {
			t.Fatal(err)
		}
	}()

	// Run `srcgraph make`.
	var buf bytes.Buffer
	w := io.MultiWriter(&buf, os.Stderr)
	cmd = exec.Command("srcgraph", "-v", "make")
	cmd.Stderr, cmd.Stdout = w, w
	cmd.Dir = repoDir

	err := cmd.Run()
	if err != nil {
		t.Errorf("Command %v in %s failed: %s.\n\nOutput was:\n%s", cmd.Args, repoDir, err, buf.String())
		return
	}

	if *mode == "gen" {
		t.Errorf("Successfully generated expected output for %s in %s. (Triggering test error so you won't mistakenly interpret a 0 return code as a test success. Run without -test.mode=gen to run the test.)", repoName, wantOutputDir)
	} else {
		checkResults(t, repoName, gotOutputDir, wantOutputDir)
	}
}

func checkResults(t *testing.T, repoName, gotOutputDir, wantOutputDir string) {
	out, err := exec.Command("diff", "-ur", wantOutputDir, gotOutputDir).CombinedOutput()
	if err != nil || len(out) > 0 {
		t.Errorf("Diff failed for %s: %s.", repoName, err)
		if len(out) > 0 {
			t.Log(string(colorizeDiff(out)))
			t.Errorf("Output for %s differed from expected.", repoName)
		}
	} else {
		t.Logf(brush.Green(repoName + " PASS").String())
	}
}

func getHEADOrTipCommitID(t *testing.T, repoDir string) string {
	// TODO(sqs): assumes git
	cmd := exec.Command("git", "rev-parse", "HEAD")
	cmd.Dir = repoDir
	out, err := cmd.Output()
	if err != nil {
		t.Fatal(err)
	}
	return string(bytes.TrimSpace(out))
}

func colorizeDiff(diff []byte) []byte {
	lines := bytes.Split(diff, []byte{'\n'})
	for i, line := range lines {
		log.Printf("XXXXXX %q", line)
		if bytes.HasPrefix(line, []byte{'-'}) {
			lines[i] = []byte(brush.Red(string(line)).String())
		}
		if bytes.HasPrefix(line, []byte{'+'}) {
			lines[i] = []byte(brush.Green(string(line)).String())
		}
	}
	return bytes.Join(lines, []byte{'\n'})
}
<commit_msg>print errors immediately and parallelize<commit_after>package srcgraph

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"

	"sourcegraph.com/sourcegraph/srcgraph/buildstore"

	"testing"

	"github.com/aybabtme/color/brush"
)

var mode = flag.String("test.mode", "test", "[test|keep|gen] 'test' runs test as normal; keep keeps around generated test files for inspection after tests complete; 'gen' generates new expected test data")
var repoMatch = flag.String("test.repo", "", "only test `srcgraph make` for repos that contain this string")

func TestMakeCmd(t *testing.T) {
	// Since we exec `srcgraph`, make sure it's up-to-date.
	if out, err := exec.Command("make", "-C", "..", "srcgraph").CombinedOutput(); err != nil {
		t.Errorf("Failed to build srcgraph for `srcgraph make` tests: %s.\n\nOutput was:\n%s", err, out)
		return
	}

	const testReposDir = "testdata/repos"
	fis, err := ioutil.ReadDir("testdata/repos")
	if err != nil {
		t.Fatal(err)
	}
	n := 0
	var wg sync.WaitGroup
	for _, fi := range fis {
		if repoDir := fi.Name(); strings.Contains(repoDir, *repoMatch) {
			fullRepoDir := filepath.Join(testReposDir, repoDir)
			wg.Add(1)
			go func() {
				defer wg.Done()
				testMakeCmd(t, fullRepoDir)
			}()
			n++
		}
	}
	wg.Wait()
	if n == 0 {
		t.Errorf("No TestMakeCmd cases were run. Did none match your constraint -test.match=%q?", *repoMatch)
	}
}

func testMakeCmd(t *testing.T, repoDir string) {
	// Update the git submodule automatically.
	cmd := exec.Command("git", "submodule", "update", filepath.Join("srcgraph", repoDir))
	cmd.Dir = ".."
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Errorf("Failed to update git submodule for %s: %s.\n\nOutput was:\n%s", repoDir, err, out)
		return
	}

	repoName := filepath.Base(repoDir)

	// Directories for the actual ("got") and expected ("want") outputs.
	gotOutputDir := filepath.Join(repoDir, "../../repos-output/got", repoName)
	wantOutputDir := filepath.Join(repoDir, "../../repos-output/want", repoName)

	// Determine and wipe the desired output dir.
	var outputDir string
	if *mode == "gen" {
		outputDir = wantOutputDir
	} else {
		outputDir = gotOutputDir
	}
	outputDir, _ = filepath.Abs(outputDir)
	if err := os.RemoveAll(outputDir); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		t.Fatal(err)
	}

	// Symlink ${repoDir}/.sourcegraph-data/${commitID} to the desired output dir.
	origOutputDestDir := filepath.Join(repoDir, buildstore.BuildDataDirName, getHEADOrTipCommitID(t, repoDir))
	if err := os.Mkdir(filepath.Dir(origOutputDestDir), 0755); err != nil && !os.IsExist(err) {
		t.Fatal(err)
	}
	if err := os.Remove(origOutputDestDir); err != nil && !os.IsNotExist(err) {
		t.Fatal(err)
	}
	if err := os.Symlink(outputDir, origOutputDestDir); err != nil {
		t.Error(err)
		return
	}
	defer func() {
		// Remove the symlink when we're done so the repo doesn't have
		// uncommitted changes.
		if err := os.Remove(origOutputDestDir); err != nil {
			t.Fatal(err)
		}
	}()

	// Run `srcgraph make`.
	var w io.Writer
	var buf bytes.Buffer
	if testing.Verbose() {
		w = io.MultiWriter(&buf, os.Stderr)
	} else {
		w = &buf
	}
	cmd = exec.Command("srcgraph", "-v", "make")
	cmd.Stderr, cmd.Stdout = w, w
	cmd.Dir = repoDir

	err := cmd.Run()
	if err != nil {
		t.Errorf("Command %v in %s failed: %s.\n\nOutput was:\n%s", cmd.Args, repoDir, err, buf.String())
		return
	}

	if *mode == "gen" {
		t.Errorf("Successfully generated expected output for %s in %s. (Triggering test error so you won't mistakenly interpret a 0 return code as a test success. Run without -test.mode=gen to run the test.)", repoName, wantOutputDir)
	} else {
		checkResults(t, buf, repoName, gotOutputDir, wantOutputDir)
	}
}

func checkResults(t *testing.T, output bytes.Buffer, repoName, gotOutputDir, wantOutputDir string) {
	out, err := exec.Command("diff", "-ur", wantOutputDir, gotOutputDir).CombinedOutput()
	if err != nil || len(out) > 0 {
		t.Logf(brush.Red(repoName + " FAIL").String())
		t.Errorf("Diff failed for %s: %s.", repoName, err)
		if len(out) > 0 {
			fmt.Println(brush.Red(repoName + "FAIL"))
			fmt.Println(output.String())
			fmt.Println(string(colorizeDiff(out)))
			t.Errorf("Output for %s differed from expected.", repoName)
		}
	} else {
		t.Logf(brush.Green(repoName + " PASS").String())
	}
}

func getHEADOrTipCommitID(t *testing.T, repoDir string) string {
	// TODO(sqs): assumes git
	cmd := exec.Command("git", "rev-parse", "HEAD")
	cmd.Dir = repoDir
	out, err := cmd.Output()
	if err != nil {
		t.Fatal(err)
	}
	return string(bytes.TrimSpace(out))
}

func colorizeDiff(diff []byte) []byte {
	lines := bytes.Split(diff, []byte{'\n'})
	for i, line := range lines {
		if bytes.HasPrefix(line, []byte{'-'}) {
			lines[i] = []byte(brush.Red(string(line)).String())
		}
		if bytes.HasPrefix(line, []byte{'+'}) {
			lines[i] = []byte(brush.Green(string(line)).String())
		}
	}
	return bytes.Join(lines, []byte{'\n'})
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package e2e

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"go.etcd.io/etcd/pkg/v3/expect"
)

const exampleConfigFile = "../../etcd.conf.yml.sample"

func TestEtcdExampleConfig(t *testing.T) {
	skipInShortMode(t)

	proc, err := spawnCmd([]string{binDir + "/etcd", "--config-file", exampleConfigFile})
	if err != nil {
		t.Fatal(err)
	}
	if err = waitReadyExpectProc(proc, etcdServerReadyLines); err != nil {
		t.Fatal(err)
	}
	if err = proc.Stop(); err != nil {
		t.Fatal(err)
	}
}

func TestEtcdMultiPeer(t *testing.T) {
	skipInShortMode(t)

	peers, tmpdirs := make([]string, 3), make([]string, 3)
	for i := range peers {
		peers[i] = fmt.Sprintf("e%d=http://127.0.0.1:%d", i, etcdProcessBasePort+i)
		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
		if err != nil {
			t.Fatal(err)
		}
		tmpdirs[i] = d
	}
	ic := strings.Join(peers, ",")

	procs := make([]*expect.ExpectProcess, len(peers))
	defer func() {
		for i := range procs {
			if procs[i] != nil {
				procs[i].Stop()
			}
			os.RemoveAll(tmpdirs[i])
		}
	}()
	for i := range procs {
		args := []string{
			binDir + "/etcd",
			"--name", fmt.Sprintf("e%d", i),
			"--listen-client-urls", "http://0.0.0.0:0",
			"--data-dir", tmpdirs[i],
			"--advertise-client-urls", "http://0.0.0.0:0",
			"--listen-peer-urls", fmt.Sprintf("http://127.0.0.1:%d,http://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
			"--initial-advertise-peer-urls", fmt.Sprintf("http://127.0.0.1:%d", etcdProcessBasePort+i),
			"--initial-cluster", ic,
		}
		p, err := spawnCmd(args)
		if err != nil {
			t.Fatal(err)
		}
		procs[i] = p
	}

	for _, p := range procs {
		if err := waitReadyExpectProc(p, etcdServerReadyLines); err != nil {
			t.Fatal(err)
		}
	}
}

// TestEtcdUnixPeers checks that etcd will boot with unix socket peers.
func TestEtcdUnixPeers(t *testing.T) {
	skipInShortMode(t)

	d, err := ioutil.TempDir("", "e1.etcd")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(d)
	proc, err := spawnCmd(
		[]string{
			binDir + "/etcd",
			"--data-dir", d,
			"--name", "e1",
			"--listen-peer-urls", "unix://etcd.unix:1",
			"--initial-advertise-peer-urls", "unix://etcd.unix:1",
			"--initial-cluster", "e1=unix://etcd.unix:1",
		},
	)
	defer os.Remove("etcd.unix:1")
	if err != nil {
		t.Fatal(err)
	}
	if err = waitReadyExpectProc(proc, etcdServerReadyLines); err != nil {
		t.Fatal(err)
	}
	if err = proc.Stop(); err != nil {
		t.Fatal(err)
	}
}

// TestEtcdPeerCNAuth checks that the inter peer auth based on CN of cert is working correctly.
func TestEtcdPeerCNAuth(t *testing.T) {
	skipInShortMode(t)

	peers, tmpdirs := make([]string, 3), make([]string, 3)
	for i := range peers {
		peers[i] = fmt.Sprintf("e%d=https://127.0.0.1:%d", i, etcdProcessBasePort+i)
		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
		if err != nil {
			t.Fatal(err)
		}
		tmpdirs[i] = d
	}
	ic := strings.Join(peers, ",")

	procs := make([]*expect.ExpectProcess, len(peers))
	defer func() {
		for i := range procs {
			if procs[i] != nil {
				procs[i].Stop()
			}
			os.RemoveAll(tmpdirs[i])
		}
	}()

	// node 0 and 1 have a cert with the correct CN, node 2 doesn't
	for i := range procs {
		commonArgs := []string{
			binDir + "/etcd",
			"--name", fmt.Sprintf("e%d", i),
			"--listen-client-urls", "http://0.0.0.0:0",
			"--data-dir", tmpdirs[i],
			"--advertise-client-urls", "http://0.0.0.0:0",
			"--listen-peer-urls", fmt.Sprintf("https://127.0.0.1:%d,https://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
			"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort+i),
			"--initial-cluster", ic,
		}

		var args []string
		if i <= 1 {
			args = []string{
				"--peer-cert-file", certPath,
				"--peer-key-file", privateKeyPath,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-cn", "example.com",
			}
		} else {
			args = []string{
				"--peer-cert-file", certPath2,
				"--peer-key-file", privateKeyPath2,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-cn", "example2.com",
			}
		}

		commonArgs = append(commonArgs, args...)

		p, err := spawnCmd(commonArgs)
		if err != nil {
			t.Fatal(err)
		}
		procs[i] = p
	}

	for i, p := range procs {
		var expect []string
		if i <= 1 {
			expect = etcdServerReadyLines
		} else {
			expect = []string{"remote error: tls: bad certificate"}
		}
		if err := waitReadyExpectProc(p, expect); err != nil {
			t.Fatal(err)
		}
	}
}

// TestEtcdPeerNameAuth checks that the inter peer auth based on cert name validation is working correctly.
func TestEtcdPeerNameAuth(t *testing.T) {
	skipInShortMode(t)

	peers, tmpdirs := make([]string, 3), make([]string, 3)
	for i := range peers {
		peers[i] = fmt.Sprintf("e%d=https://127.0.0.1:%d", i, etcdProcessBasePort+i)
		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
		if err != nil {
			t.Fatal(err)
		}
		tmpdirs[i] = d
	}
	ic := strings.Join(peers, ",")

	procs := make([]*expect.ExpectProcess, len(peers))
	defer func() {
		for i := range procs {
			if procs[i] != nil {
				procs[i].Stop()
			}
			os.RemoveAll(tmpdirs[i])
		}
	}()

	// node 0 and 1 have a cert with the correct certificate name, node 2 doesn't
	for i := range procs {
		commonArgs := []string{
			binDir + "/etcd",
			"--name", fmt.Sprintf("e%d", i),
			"--listen-client-urls", "http://0.0.0.0:0",
			"--data-dir", tmpdirs[i],
			"--advertise-client-urls", "http://0.0.0.0:0",
			"--listen-peer-urls", fmt.Sprintf("https://127.0.0.1:%d,https://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
			"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort+i),
			"--initial-cluster", ic,
		}

		var args []string
		if i <= 1 {
			args = []string{
				"--peer-cert-file", certPath,
				"--peer-key-file", privateKeyPath,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-hostname", "localhost",
			}
		} else {
			args = []string{
				"--peer-cert-file", certPath2,
				"--peer-key-file", privateKeyPath2,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-hostname", "example2.com",
			}
		}

		commonArgs = append(commonArgs, args...)

		p, err := spawnCmd(commonArgs)
		if err != nil {
			t.Fatal(err)
		}
		procs[i] = p
	}

	for i, p := range procs {
		var expect []string
		if i <= 1 {
			expect = etcdServerReadyLines
		} else {
			expect = []string{"client certificate authentication failed"}
		}
		if err := waitReadyExpectProc(p, expect); err != nil {
			t.Fatal(err)
		}
	}
}

func TestGrpcproxyAndCommonName(t *testing.T) {
	skipInShortMode(t)

	argsWithNonEmptyCN := []string{
		binDir + "/etcd",
		"grpc-proxy",
		"start",
		"--cert", certPath2,
		"--key", privateKeyPath2,
		"--cacert", caPath,
	}

	argsWithEmptyCN := []string{
		binDir + "/etcd",
		"grpc-proxy",
		"start",
		"--cert", certPath3,
		"--key", privateKeyPath3,
		"--cacert", caPath,
	}

	err := spawnWithExpect(argsWithNonEmptyCN, "cert has non empty Common Name")
	if err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	p, err := spawnCmd(argsWithEmptyCN)
	if err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
	p.Stop()
}
<commit_msg>tests: fix test case panic error<commit_after>// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package e2e

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"go.etcd.io/etcd/pkg/v3/expect"
)

const exampleConfigFile = "../../etcd.conf.yml.sample"

func TestEtcdExampleConfig(t *testing.T) {
	skipInShortMode(t)

	proc, err := spawnCmd([]string{binDir + "/etcd", "--config-file", exampleConfigFile})
	if err != nil {
		t.Fatal(err)
	}
	if err = waitReadyExpectProc(proc, etcdServerReadyLines); err != nil {
		t.Fatal(err)
	}
	if err = proc.Stop(); err != nil {
		t.Fatal(err)
	}
}

func TestEtcdMultiPeer(t *testing.T) {
	skipInShortMode(t)

	peers, tmpdirs := make([]string, 3), make([]string, 3)
	for i := range peers {
		peers[i] = fmt.Sprintf("e%d=http://127.0.0.1:%d", i, etcdProcessBasePort+i)
		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
		if err != nil {
			t.Fatal(err)
		}
		tmpdirs[i] = d
	}
	ic := strings.Join(peers, ",")

	procs := make([]*expect.ExpectProcess, len(peers))
	defer func() {
		for i := range procs {
			if procs[i] != nil {
				procs[i].Stop()
			}
			os.RemoveAll(tmpdirs[i])
		}
	}()
	for i := range procs {
		args := []string{
			binDir + "/etcd",
			"--name", fmt.Sprintf("e%d", i),
			"--listen-client-urls", "http://0.0.0.0:0",
			"--data-dir", tmpdirs[i],
			"--advertise-client-urls", "http://0.0.0.0:0",
			"--listen-peer-urls", fmt.Sprintf("http://127.0.0.1:%d,http://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
			"--initial-advertise-peer-urls", fmt.Sprintf("http://127.0.0.1:%d", etcdProcessBasePort+i),
			"--initial-cluster", ic,
		}
		p, err := spawnCmd(args)
		if err != nil {
			t.Fatal(err)
		}
		procs[i] = p
	}

	for _, p := range procs {
		if err := waitReadyExpectProc(p, etcdServerReadyLines); err != nil {
			t.Fatal(err)
		}
	}
}

// TestEtcdUnixPeers checks that etcd will boot with unix socket peers.
func TestEtcdUnixPeers(t *testing.T) {
	skipInShortMode(t)

	d, err := ioutil.TempDir("", "e1.etcd")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(d)
	proc, err := spawnCmd(
		[]string{
			binDir + "/etcd",
			"--data-dir", d,
			"--name", "e1",
			"--listen-peer-urls", "unix://etcd.unix:1",
			"--initial-advertise-peer-urls", "unix://etcd.unix:1",
			"--initial-cluster", "e1=unix://etcd.unix:1",
		},
	)
	defer os.Remove("etcd.unix:1")
	if err != nil {
		t.Fatal(err)
	}
	if err = waitReadyExpectProc(proc, etcdServerReadyLines); err != nil {
		t.Fatal(err)
	}
	if err = proc.Stop(); err != nil {
		t.Fatal(err)
	}
}

// TestEtcdPeerCNAuth checks that the inter peer auth based on CN of cert is working correctly.
func TestEtcdPeerCNAuth(t *testing.T) {
	skipInShortMode(t)

	peers, tmpdirs := make([]string, 3), make([]string, 3)
	for i := range peers {
		peers[i] = fmt.Sprintf("e%d=https://127.0.0.1:%d", i, etcdProcessBasePort+i)
		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
		if err != nil {
			t.Fatal(err)
		}
		tmpdirs[i] = d
	}
	ic := strings.Join(peers, ",")

	procs := make([]*expect.ExpectProcess, len(peers))
	defer func() {
		for i := range procs {
			if procs[i] != nil {
				procs[i].Stop()
			}
			os.RemoveAll(tmpdirs[i])
		}
	}()

	// node 0 and 1 have a cert with the correct CN, node 2 doesn't
	for i := range procs {
		commonArgs := []string{
			binDir + "/etcd",
			"--name", fmt.Sprintf("e%d", i),
			"--listen-client-urls", "http://0.0.0.0:0",
			"--data-dir", tmpdirs[i],
			"--advertise-client-urls", "http://0.0.0.0:0",
			"--listen-peer-urls", fmt.Sprintf("https://127.0.0.1:%d,https://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
			"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort+i),
			"--initial-cluster", ic,
		}

		var args []string
		if i <= 1 {
			args = []string{
				"--peer-cert-file", certPath,
				"--peer-key-file", privateKeyPath,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-cn", "example.com",
			}
		} else {
			args = []string{
				"--peer-cert-file", certPath2,
				"--peer-key-file", privateKeyPath2,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-cn", "example2.com",
			}
		}

		commonArgs = append(commonArgs, args...)

		p, err := spawnCmd(commonArgs)
		if err != nil {
			t.Fatal(err)
		}
		procs[i] = p
	}

	for i, p := range procs {
		var expect []string
		if i <= 1 {
			expect = etcdServerReadyLines
		} else {
			expect = []string{"remote error: tls: bad certificate"}
		}
		if err := waitReadyExpectProc(p, expect); err != nil {
			t.Fatal(err)
		}
	}
}

// TestEtcdPeerNameAuth checks that the inter peer auth based on cert name validation is working correctly.
func TestEtcdPeerNameAuth(t *testing.T) {
	skipInShortMode(t)

	peers, tmpdirs := make([]string, 3), make([]string, 3)
	for i := range peers {
		peers[i] = fmt.Sprintf("e%d=https://127.0.0.1:%d", i, etcdProcessBasePort+i)
		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
		if err != nil {
			t.Fatal(err)
		}
		tmpdirs[i] = d
	}
	ic := strings.Join(peers, ",")

	procs := make([]*expect.ExpectProcess, len(peers))
	defer func() {
		for i := range procs {
			if procs[i] != nil {
				procs[i].Stop()
			}
			os.RemoveAll(tmpdirs[i])
		}
	}()

	// node 0 and 1 have a cert with the correct certificate name, node 2 doesn't
	for i := range procs {
		commonArgs := []string{
			binDir + "/etcd",
			"--name", fmt.Sprintf("e%d", i),
			"--listen-client-urls", "http://0.0.0.0:0",
			"--data-dir", tmpdirs[i],
			"--advertise-client-urls", "http://0.0.0.0:0",
			"--listen-peer-urls", fmt.Sprintf("https://127.0.0.1:%d,https://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
			"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort+i),
			"--initial-cluster", ic,
		}

		var args []string
		if i <= 1 {
			args = []string{
				"--peer-cert-file", certPath,
				"--peer-key-file", privateKeyPath,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-hostname", "localhost",
			}
		} else {
			args = []string{
				"--peer-cert-file", certPath2,
				"--peer-key-file", privateKeyPath2,
				"--peer-trusted-ca-file", caPath,
				"--peer-client-cert-auth",
				"--peer-cert-allowed-hostname", "example2.com",
			}
		}

		commonArgs = append(commonArgs, args...)

		p, err := spawnCmd(commonArgs)
		if err != nil {
			t.Fatal(err)
		}
		procs[i] = p
	}

	for i, p := range procs {
		var expect []string
		if i <= 1 {
			expect = etcdServerReadyLines
		} else {
			expect = []string{"client certificate authentication failed"}
		}
		if err := waitReadyExpectProc(p, expect); err != nil {
			t.Fatal(err)
		}
	}
}

func TestGrpcproxyAndCommonName(t *testing.T) {
	skipInShortMode(t)

	argsWithNonEmptyCN := []string{
		binDir + "/etcd",
		"grpc-proxy",
		"start",
		"--cert", certPath2,
		"--key", privateKeyPath2,
		"--cacert", caPath,
	}

	argsWithEmptyCN := []string{
		binDir + "/etcd",
		"grpc-proxy",
		"start",
		"--cert", certPath3,
		"--key", privateKeyPath3,
		"--cacert", caPath,
	}

	err := spawnWithExpect(argsWithNonEmptyCN, "cert has non empty Common Name")
	if err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	p, err := spawnCmd(argsWithEmptyCN)
	defer func() {
		if p != nil {
			p.Stop()
		}
	}()

	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>