<commit_before>// Package flowgraph for scalable asynchronous system development
// https://github.com/vectaport/flowgraph/wiki
package flowgraph

import (
	"errors"
	"fmt"
	"github.com/vectaport/fgbase"
)

/*=====================================================================*/

// End of flow
var EOF = errors.New("EOF")

/*=====================================================================*/

// Getter receives a value with the Get method. Use Hub.Tracef for tracing.
type Getter interface {
	Get(h Hub) (interface{}, error)
}

// Putter transmits a value with the Put method. Use Hub.Tracef
// for tracing
type Putter interface {
	Put(h Hub, v interface{}) error
}

// Transformer transforms a variadic list of values into a slice
// of values with the Transform method. Use Hub.Tracef for tracing.
type Transformer interface {
	Transform(h Hub, c ...interface{}) ([]interface{}, error)
}

/*=====================================================================*/

// Flowgraph interface for flowgraphs assembled out of hubs and streams
type Flowgraph interface {

	// Name returns the name of this flowgraph
	Name() string

	// Hub returns a hub by index
	Hub(i int) Hub
	// Stream returns a stream by index
	Stream(i int) Stream

	// NumHub returns the number of hubs
	NumHub() int
	// NumStream returns the number of streams
	NumStream() int

	// FindHub finds a hub by name
	FindHub(name string) Hub
	// FindStream finds a stream by name
	FindStream(name string) Stream

	// NewHub returns a new uninitialized hub
	NewHub(name, code string) Hub
	// NewStream returns a new uninitialized stream
	NewStream(name string) Stream

	// Connect connects two hubs via named ports
	Connect(upstream Hub, upstreamPort string, dnstream Hub, dnstreamPort string) Stream

	// Run runs the flowgraph
	Run()

	/* POTENTIALLY OBSOLETE API AFTER HERE -- BOUND TO BE RESTRUCTURED */

	// InsertHub adds a Hub to the flowgraph, connecting inputs to existing
	// dangling streams as available and creating dangling output streams as needed.
	InsertHub(h Hub)

	// NewIncoming creates an input hub that uses a Getter
	NewIncoming(name string, getter Getter) Hub
	// InsertIncoming adds an input source that uses a Getter
	InsertIncoming(name string, getter Getter) Hub

	// InsertOutgoing adds an output destination that uses a Putter
	InsertOutgoing(name string, putter Putter) Hub

	// InsertConst adds an input constant as an incoming source.
	InsertConst(name string, v interface{}) Hub
	// InsertArray adds an array as an incoming source.
	InsertArray(name string, arr []interface{}) Hub

	// NewSink creates an output sink hub
	NewSink(name string) Hub
	// InsertSink adds an output sink
	InsertSink(name string) Hub

	// InsertAllOf adds a transform that waits for all inputs before producing outputs
	InsertAllOf(name string, transformer Transformer) Hub
}

// implementation of Flowgraph
type graph struct {
	name         string
	hubs         []*fgbase.Node
	streams      []*fgbase.Edge
	nameToHub    map[string]*fgbase.Node
	nameToStream map[string]*fgbase.Edge
}

// New returns a named flowgraph implemented with the fgbase package
func New(nm string) Flowgraph {
	nameToHub := make(map[string]*fgbase.Node)
	nameToStream := make(map[string]*fgbase.Edge)
	return &graph{nm, nil, nil, nameToHub, nameToStream}
}

// Name returns the name of this flowgraph
func (fg *graph) Name() string {
	return fg.Name()
}

// Hub returns a hub by index
func (fg *graph) Hub(n int) Hub {
	return hub{fg.hubs[n]}
}

// Stream returns a stream by index
func (fg *graph) Stream(n int) Stream {
	return stream{fg.streams[n]}
}

// NumHub returns the number of hubs
func (fg *graph) NumHub() int {
	return len(fg.hubs)
}

// NumStream returns the number of hubs
func (fg *graph) NumStream() int {
	return len(fg.streams)
}

// NewHub returns a new uninitialized hub
func (fg *graph) NewHub(name, code string) Hub {
	n := fgbase.MakeNode(name, nil, nil, nil, nil)
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{&n}
}

// NewStream returns a new uninitialized stream
func (fg *graph) NewStream(name string) Stream {
	if name == "" {
		name = fmt.Sprintf("e%d", len(fg.streams))
	}
	e := fgbase.MakeEdge(name, nil)
	fg.streams = append(fg.streams, &e)
	fg.nameToStream[name] = &e
	return stream{&e}
}

// FindHub finds a hub by name
func (fg *graph) FindHub(name string) Hub {
	return hub{fg.nameToHub[name]}
}

// FindStream finds a Stream by name
func (fg *graph) FindStream(name string) Stream {
	return stream{fg.nameToStream[name]}
}

// Connect connects two hubs via named ports
func (fg *graph) Connect(
	upstream Hub, upstreamPort string,
	dnstream Hub, dnstreamPort string) Stream {

	usEdge, usok := upstream.Base().(*fgbase.Node).FindDst(upstreamPort)
	dsEdge, dsok := dnstream.Base().(*fgbase.Node).FindSrc(dnstreamPort)
	if !usok || !dsok {
		return stream{nil}
	}
	if usEdge == nil && dsEdge == nil {
		e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
		fg.streams = append(fg.streams, &e)
		eup := e
		edn := e
		upstream.SetDestination(upstreamPort, stream{&eup})
		dnstream.SetSource(dnstreamPort, stream{&edn})
		return stream{&e}
	}
	return nil
}

// Run runs the flowgraph
func (fg *graph) Run() {
	fgbase.RunGraph(fg.hubs)
}

// InsertHub adds a Hub to the flowgraph, connecting inputs to existing
// dangling streams as available and creating dangling output streams as needed.
func (fg *graph) InsertHub(h Hub) {
	fmt.Printf("CALL MADE TO INSERTHUB\n")

	i := 0

	nextDanglingSrcStream :=
		func() *fgbase.Edge {
			for ; i < len(fg.streams) && fg.streams[i].DstCnt() != 0; i++ {
			}

			if i == len(fg.streams) {
				return nil // or makeStream?
			} else {
				return fg.streams[i]
			}
		}

	// connect to input streams
	for j := 0; j < h.NumSource(); j++ {
		if h.Source(j) == nil {
			p := nextDanglingSrcStream()
			fmt.Printf("p is now %v\n", p)
		}
	}

	// create output streams

}

// NewIncoming adds an incoming source that uses a Getter
func (fg *graph) NewIncoming(name string, getter Getter) Hub {
	n := funcIncoming(fgbase.Edge{}, getter)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertIncoming adds an incoming source that uses a Getter
func (fg *graph) InsertIncoming(name string, getter Getter) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := funcIncoming(e, getter)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertOutgoing adds a destination that uses a Putter
func (fg *graph) InsertOutgoing(name string, putter Putter) Hub {
	n := funcOutgoing(*fg.streams[len(fg.streams)-1], putter)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertConst adds an input constant as an incoming source.
func (fg *graph) InsertConst(name string, v interface{}) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := fgbase.FuncConst(e, v)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertArray adds an array as an incoming source.
func (fg *graph) InsertArray(name string, arr []interface{}) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := fgbase.FuncArray(e, arr)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// NewSink creates an output sink hub
func (fg *graph) NewSink(name string) Hub {
	n := fgbase.FuncSink(fgbase.Edge{})
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertSink adds a output sink on the latest stream
func (fg *graph) InsertSink(name string) Hub {
	i := len(fg.streams) - 1
	n := fgbase.FuncSink(*fg.streams[i])
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertAllOf adds a transform that waits for all inputs before producing outputs
func (fg *graph) InsertAllOf(name string, transformer Transformer) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := funcAllOf([]fgbase.Edge{*fg.streams[len(fg.streams)-2]}, []fgbase.Edge{*fg.streams[len(fg.streams)-1]},
		name, transformer)
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}
<commit_msg>multi-line Connect<commit_after>// Package flowgraph for scalable asynchronous system development
// https://github.com/vectaport/flowgraph/wiki
package flowgraph

import (
	"errors"
	"fmt"
	"github.com/vectaport/fgbase"
)

/*=====================================================================*/

// End of flow
var EOF = errors.New("EOF")

/*=====================================================================*/

// Getter receives a value with the Get method. Use Hub.Tracef for tracing.
type Getter interface {
	Get(h Hub) (interface{}, error)
}

// Putter transmits a value with the Put method. Use Hub.Tracef
// for tracing
type Putter interface {
	Put(h Hub, v interface{}) error
}

// Transformer transforms a variadic list of values into a slice
// of values with the Transform method. Use Hub.Tracef for tracing.
type Transformer interface {
	Transform(h Hub, c ...interface{}) ([]interface{}, error)
}

/*=====================================================================*/

// Flowgraph interface for flowgraphs assembled out of hubs and streams
type Flowgraph interface {

	// Name returns the name of this flowgraph
	Name() string

	// Hub returns a hub by index
	Hub(i int) Hub
	// Stream returns a stream by index
	Stream(i int) Stream

	// NumHub returns the number of hubs
	NumHub() int
	// NumStream returns the number of streams
	NumStream() int

	// FindHub finds a hub by name
	FindHub(name string) Hub
	// FindStream finds a stream by name
	FindStream(name string) Stream

	// NewHub returns a new uninitialized hub
	NewHub(name, code string) Hub
	// NewStream returns a new uninitialized stream
	NewStream(name string) Stream

	// Connect connects two hubs via named ports
	Connect(upstream Hub, upstreamPort string,
		dnstream Hub, dnstreamPort string) Stream

	// Run runs the flowgraph
	Run()

	/* POTENTIALLY OBSOLETE API AFTER HERE -- BOUND TO BE RESTRUCTURED */

	// InsertHub adds a Hub to the flowgraph, connecting inputs to existing
	// dangling streams as available and creating dangling output streams as needed.
	InsertHub(h Hub)

	// NewIncoming creates an input hub that uses a Getter
	NewIncoming(name string, getter Getter) Hub
	// InsertIncoming adds an input source that uses a Getter
	InsertIncoming(name string, getter Getter) Hub

	// InsertOutgoing adds an output destination that uses a Putter
	InsertOutgoing(name string, putter Putter) Hub

	// InsertConst adds an input constant as an incoming source.
	InsertConst(name string, v interface{}) Hub
	// InsertArray adds an array as an incoming source.
	InsertArray(name string, arr []interface{}) Hub

	// NewSink creates an output sink hub
	NewSink(name string) Hub
	// InsertSink adds an output sink
	InsertSink(name string) Hub

	// InsertAllOf adds a transform that waits for all inputs before producing outputs
	InsertAllOf(name string, transformer Transformer) Hub
}

// implementation of Flowgraph
type graph struct {
	name         string
	hubs         []*fgbase.Node
	streams      []*fgbase.Edge
	nameToHub    map[string]*fgbase.Node
	nameToStream map[string]*fgbase.Edge
}

// New returns a named flowgraph implemented with the fgbase package
func New(nm string) Flowgraph {
	nameToHub := make(map[string]*fgbase.Node)
	nameToStream := make(map[string]*fgbase.Edge)
	return &graph{nm, nil, nil, nameToHub, nameToStream}
}

// Name returns the name of this flowgraph
func (fg *graph) Name() string {
	return fg.Name()
}

// Hub returns a hub by index
func (fg *graph) Hub(n int) Hub {
	return hub{fg.hubs[n]}
}

// Stream returns a stream by index
func (fg *graph) Stream(n int) Stream {
	return stream{fg.streams[n]}
}

// NumHub returns the number of hubs
func (fg *graph) NumHub() int {
	return len(fg.hubs)
}

// NumStream returns the number of hubs
func (fg *graph) NumStream() int {
	return len(fg.streams)
}

// NewHub returns a new uninitialized hub
func (fg *graph) NewHub(name, code string) Hub {
	n := fgbase.MakeNode(name, nil, nil, nil, nil)
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{&n}
}

// NewStream returns a new uninitialized stream
func (fg *graph) NewStream(name string) Stream {
	if name == "" {
		name = fmt.Sprintf("e%d", len(fg.streams))
	}
	e := fgbase.MakeEdge(name, nil)
	fg.streams = append(fg.streams, &e)
	fg.nameToStream[name] = &e
	return stream{&e}
}

// FindHub finds a hub by name
func (fg *graph) FindHub(name string) Hub {
	return hub{fg.nameToHub[name]}
}

// FindStream finds a Stream by name
func (fg *graph) FindStream(name string) Stream {
	return stream{fg.nameToStream[name]}
}

// Connect connects two hubs via named ports
func (fg *graph) Connect(
	upstream Hub, upstreamPort string,
	dnstream Hub, dnstreamPort string) Stream {

	usEdge, usok := upstream.Base().(*fgbase.Node).FindDst(upstreamPort)
	dsEdge, dsok := dnstream.Base().(*fgbase.Node).FindSrc(dnstreamPort)
	if !usok || !dsok {
		return stream{nil}
	}
	if usEdge == nil && dsEdge == nil {
		e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
		fg.streams = append(fg.streams, &e)
		eup := e
		edn := e
		upstream.SetDestination(upstreamPort, stream{&eup})
		dnstream.SetSource(dnstreamPort, stream{&edn})
		return stream{&e}
	}
	return nil
}

// Run runs the flowgraph
func (fg *graph) Run() {
	fgbase.RunGraph(fg.hubs)
}

// InsertHub adds a Hub to the flowgraph, connecting inputs to existing
// dangling streams as available and creating dangling output streams as needed.
func (fg *graph) InsertHub(h Hub) {
	fmt.Printf("CALL MADE TO INSERTHUB\n")

	i := 0

	nextDanglingSrcStream :=
		func() *fgbase.Edge {
			for ; i < len(fg.streams) && fg.streams[i].DstCnt() != 0; i++ {
			}

			if i == len(fg.streams) {
				return nil // or makeStream?
			} else {
				return fg.streams[i]
			}
		}

	// connect to input streams
	for j := 0; j < h.NumSource(); j++ {
		if h.Source(j) == nil {
			p := nextDanglingSrcStream()
			fmt.Printf("p is now %v\n", p)
		}
	}

	// create output streams

}

// NewIncoming adds an incoming source that uses a Getter
func (fg *graph) NewIncoming(name string, getter Getter) Hub {
	n := funcIncoming(fgbase.Edge{}, getter)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertIncoming adds an incoming source that uses a Getter
func (fg *graph) InsertIncoming(name string, getter Getter) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := funcIncoming(e, getter)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertOutgoing adds a destination that uses a Putter
func (fg *graph) InsertOutgoing(name string, putter Putter) Hub {
	n := funcOutgoing(*fg.streams[len(fg.streams)-1], putter)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertConst adds an input constant as an incoming source.
func (fg *graph) InsertConst(name string, v interface{}) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := fgbase.FuncConst(e, v)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertArray adds an array as an incoming source.
func (fg *graph) InsertArray(name string, arr []interface{}) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := fgbase.FuncArray(e, arr)
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// NewSink creates an output sink hub
func (fg *graph) NewSink(name string) Hub {
	n := fgbase.FuncSink(fgbase.Edge{})
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertSink adds a output sink on the latest stream
func (fg *graph) InsertSink(name string) Hub {
	i := len(fg.streams) - 1
	n := fgbase.FuncSink(*fg.streams[i])
	n.Name = name
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}

// InsertAllOf adds a transform that waits for all inputs before producing outputs
func (fg *graph) InsertAllOf(name string, transformer Transformer) Hub {
	e := fgbase.MakeEdge(fmt.Sprintf("e%d", len(fg.streams)), nil)
	fg.streams = append(fg.streams, &e)
	n := funcAllOf([]fgbase.Edge{*fg.streams[len(fg.streams)-2]}, []fgbase.Edge{*fg.streams[len(fg.streams)-1]},
		name, transformer)
	fg.hubs = append(fg.hubs, &n)
	fg.nameToHub[name] = &n
	return hub{fg.hubs[len(fg.hubs)-1]}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"runtime/debug"
	"testing"
)

//
// --- TESTS

func TestFindMajorVersion(t *testing.T) {
	// Should find
	versionStr := `
Xcode 7.0
Build version 7A220`

	version, err := findMajorVersion(versionStr)
	if err != nil {
		t.Fatalf("Failed to findMajorVersion: %s", err)
	}
	if version != 7 {
		t.Fatalf("Expected version (7), actual (%d)", version)
	}

	// Should not find
	versionStr = `
	Build version 7A220`

	version, err = findMajorVersion(versionStr)
	if err == nil {
		t.Fatalf("Should failed to findMajorVersion: %s", err)
	}
	if version != -1 {
		t.Fatalf("Expected version (-1), actual (%d)", version)
	}
}

func Test_isStringFoundInOutput(t *testing.T) {
	// Should NOT find
	searchPattern := "something"
	isShouldFind := false
	for _, anOutStr := range []string{
		"",
		"a",
		"1",
		"somethin",
		"somethinx",
		"TEST FAILED",
	} {
		if isFound := isStringFoundInOutput(searchPattern, anOutStr); isFound != isShouldFind {
			t.Logf("Search pattern was: %s", searchPattern)
			t.Logf("Input string to search in was: %s", anOutStr)
			t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
		}
	}

	// Should find
	searchPattern = "search for this"
	isShouldFind = true
	for _, anOutStr := range []string{
		"search for this",
		"-search for this",
		"search for this-",
		"-search for this-",
	} {
		if isFound := isStringFoundInOutput(searchPattern, anOutStr); isFound != isShouldFind {
			t.Logf("Search pattern was: %s", searchPattern)
			t.Logf("Input string to search in was: %s", anOutStr)
			t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
		}
	}

	// Should find - empty pattern - always "yes"
	searchPattern = ""
	isShouldFind = true
	for _, anOutStr := range []string{
		"",
		"a",
		"1",
		"TEST FAILED",
	} {
		if isFound := isStringFoundInOutput(searchPattern, anOutStr); isFound != isShouldFind {
			t.Logf("Search pattern was: %s", searchPattern)
			t.Logf("Input string to search in was: %s", anOutStr)
			t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
		}
	}
}

func Test_findTestSummaryInOutput(t *testing.T) {
	// load sample logs
	sampleIPhoneSimulatorLog, err := loadFileContent("./_samples/xcodebuild-iPhoneSimulator-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load error sample log : %s", err)
	}
	sampleUITestTimeoutLog, err := loadFileContent("./_samples/xcodebuild-UITest-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load (UITest timeout) error sample log : %s", err)
	}
	sampleOKBuildLog, err := loadFileContent("./_samples/xcodebuild-ok.txt")
	if err != nil {
		t.Fatalf("Failed to load xcodebuild-ok.txt : %s", err)
	}

	// Should NOT find
	for _, anOutStr := range []string{
		"",
		"xyz",
		"TEST SUCCEEDED",
		"TEST FAILED",
		sampleIPhoneSimulatorLog,
	} {
		testFindTestSummaryInOutput(t, false, anOutStr, true)
		testFindTestSummaryInOutput(t, false, anOutStr, false)
	}

	// should find
	testFindTestSummaryInOutput(t, true, "** TEST SUCCEEDED **", true)
	testFindTestSummaryInOutput(t, true, "** TEST FAILED **", false)
	testFindTestSummaryInOutput(t, true, "Failing tests:", false)
	testFindTestSummaryInOutput(t, true, "Testing failed:", false)
	testFindTestSummaryInOutput(t, true, sampleOKBuildLog, true)
	testFindTestSummaryInOutput(t, true, sampleUITestTimeoutLog, false)
}

func TestIsStringFoundInOutput_timeOutMessageIPhoneSimulator(t *testing.T) {
	// load sample logs
	sampleIPhoneSimulatorLog, err := loadFileContent("./_samples/xcodebuild-iPhoneSimulator-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load error sample log : %s", err)
	}
	sampleOKBuildLog, err := loadFileContent("./_samples/xcodebuild-ok.txt")
	if err != nil {
		t.Fatalf("Failed to load xcodebuild-ok.txt : %s", err)
	}

	// Should find
	for _, anOutStr := range []string{
		"iPhoneSimulator: Timed out waiting",
		"iphoneSimulator: timed out waiting",
		"iphoneSimulator: timed out waiting, test test test",
		"aaaiphoneSimulator: timed out waiting, test test test",
		"aaa iphoneSimulator: timed out waiting, test test test",
		sampleIPhoneSimulatorLog,
	} {
		testIPhoneSimulatorTimeoutWith(t, anOutStr, true)
	}

	// Should not
	for _, anOutStr := range []string{
		"",
		"iphoneSimulator:",
		sampleOKBuildLog,
	} {
		testIPhoneSimulatorTimeoutWith(t, anOutStr, false)
	}
}

func TestIsStringFoundInOutput_timeOutMessageUITest(t *testing.T) {
	// load sample logs
	sampleUITestTimeoutLog, err := loadFileContent("./_samples/xcodebuild-UITest-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load error sample log : %s", err)
	}
	sampleOKBuildLog, err := loadFileContent("./_samples/xcodebuild-ok.txt")
	if err != nil {
		t.Fatalf("Failed to load xcodebuild-ok.txt : %s", err)
	}

	// Should find
	for _, anOutStr := range []string{
		"Terminating app due to uncaught exception '_XCTestCaseInterruptionException'",
		"terminating app due to uncaught exception '_XCTestCaseInterruptionException'",
		"aaTerminating app due to uncaught exception '_XCTestCaseInterruptionException'aa",
		"aa Terminating app due to uncaught exception '_XCTestCaseInterruptionException' aa",
		sampleUITestTimeoutLog,
	} {
		testTimeOutMessageUITestWith(t, anOutStr, true)
	}

	// Should not
	for _, anOutStr := range []string{
		"",
		"Terminating app due to uncaught exception",
		"_XCTestCaseInterruptionException",
		sampleOKBuildLog,
	} {
		testTimeOutMessageUITestWith(t, anOutStr, false)
	}
}

func Test_printableCommandArgs(t *testing.T) {
	orgCmdArgs := []string{
		"xcodebuild", "-project", "MyProj.xcodeproj", "-scheme", "MyScheme",
		"build", "test",
		"-destination", "platform=iOS Simulator,name=iPhone 6,OS=latest",
		"-sdk", "iphonesimulator",
	}
	resStr := printableCommandArgs(orgCmdArgs)
	expectedStr := `xcodebuild "-project" "MyProj.xcodeproj" "-scheme" "MyScheme" "build" "test" "-destination" "platform=iOS Simulator,name=iPhone 6,OS=latest" "-sdk" "iphonesimulator"`

	if resStr != expectedStr {
		t.Log("printableCommandArgs failed to generate the expected string!")
		t.Logf(" -> expectedStr: %s", expectedStr)
		t.Logf(" -> resStr: %s", resStr)
		t.Fatalf("Expected string does not match the generated string. Original args: (%#v)", orgCmdArgs)
	}
}

func Test_findFirstDelimiter(t *testing.T) {
	test := func(strToSearchIn string, searchDelims []string, expectedDelim string, expectedIdx int) {
		foundIdx, foundDelim := findFirstDelimiter(strToSearchIn, searchDelims)

		if foundDelim != expectedDelim {
			t.Log("foundDelim != expectedDelim")
			t.Logf(" -> foundDelim: %s", foundDelim)
			t.Logf(" -> expectedDelim: %s", expectedDelim)
			t.Fatalf("searchDelims: (%#v) | strToSearchIn: %s", searchDelims, strToSearchIn)
		}
		if foundIdx != expectedIdx {
			t.Log("foundIdx != expectedIdx")
			t.Logf(" -> foundIdx: %v", foundIdx)
			t.Logf(" -> expectedIdx: %v", expectedIdx)
			t.Fatalf("searchDelims: (%#v) | strToSearchIn: %s", searchDelims, strToSearchIn)
		}
	}

	strToSearchIn := `Touch /Users/bitrise/Library/Developer/Xcode/DerivedData/BitriseSampleWithYML-duyungvabqzmagefbqehoqyodaqz/Build/Products/Debug-iphonesimulator/BitriseSampleWithYMLTests.xctest
    cd /Users/bitrise/develop/sample-apps-ios-with-bitrise-yml
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/heroku/bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/bitrise/develop/go/bin:/usr/local/opt/go/libexec/bin:/Users/bitrise/bin:/Users/bitrise/develop/go/bin:/usr/local/opt/go/libexec/bin:/Users/bitrise/bin"
    /usr/bin/touch -c /Users/bitrise/Library/Developer/Xcode/DerivedData/BitriseSampleWithYML-duyungvabqzmagefbqehoqyodaqz/Build/Products/Debug-iphonesimulator/BitriseSampleWithYMLTests.xctest

** TEST SUCCEEDED **

Test Suite 'All tests' started at 2015-09-20 16:01:13.995
Test Suite 'BitriseSampleWithYMLTests.xctest' started at 2015-09-20 16:01:13.997`

	// found
	// note: order of delimiters doesn't matter, first occurance will be used
	test(strToSearchIn, []string{"TEST SUCCEEDED", "Test Suite"}, "TEST SUCCEEDED", 842)
	test(strToSearchIn, []string{"Test Suite", "TEST SUCCEEDED"}, "TEST SUCCEEDED", 842)
	test("aaaa Test Suite aaa", []string{"Test Suite", "TEST SUCCEEDED"}, "Test Suite", 5)
	test("aaaa Test Suite aaa", []string{"TEST SUCCEEDED", "Test Suite"}, "Test Suite", 5)

	// not found
	test("aaaa", []string{"TEST SUCCEEDED", "Test Suite"}, "", -1)
}

//
// --- TESTING UTILITY FUNCS

func testIsFoundWith(t *testing.T, searchPattern, outputToSearchIn string, isShouldFind bool) {
	if isFound := isStringFoundInOutput(searchPattern, outputToSearchIn); isFound != isShouldFind {
		t.Logf("Search pattern was: %s", searchPattern)
		t.Logf("Input string to search in was: %s", outputToSearchIn)
		t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
	}
}
func testIPhoneSimulatorTimeoutWith(t *testing.T, outputToSearchIn string, isShouldFind bool) {
	testIsFoundWith(t, timeOutMessageIPhoneSimulator, outputToSearchIn, isShouldFind)
}

func testTimeOutMessageUITestWith(t *testing.T, outputToSearchIn string, isShouldFind bool) {
	testIsFoundWith(t, timeOutMessageUITest, outputToSearchIn, isShouldFind)
}

func testFindTestSummaryInOutput(t *testing.T, isExpectToFind bool, fullOutput string, isRunSucess bool) {
	resStr := findTestSummaryInOutput(fullOutput, isRunSucess)
	if isExpectToFind && resStr == "" {
		t.Logf("Expected to find Test Summary in provided log.")
		debug.PrintStack()
		t.Fatalf("Provided output was: %s", fullOutput)
	}
	if !isExpectToFind && resStr != "" {
		t.Logf("Expected to NOT find Test Summary in provided log.")
		debug.PrintStack()
		t.Fatalf("Provided output was: %s", fullOutput)
	}
}

func loadFileContent(filePth string) (string, error) {
	fileBytes, err := ioutil.ReadFile(filePth)
	if err != nil {
		return "", err
	}
	return string(fileBytes), nil
}
<commit_msg>fixes<commit_after>package main

import (
	"io/ioutil"
	"runtime/debug"
	"testing"
)

//
// --- TESTS

func TestFindXcodeMajorVersion(t *testing.T) {
	// Should find
	versionStr := `
Xcode 7.0
Build version 7A220`

	version, err := findXcodeMajorVersion(versionStr)
	if err != nil {
		t.Fatalf("Failed to findMajorVersion: %s", err)
	}
	if version != 7 {
		t.Fatalf("Expected version (7), actual (%d)", version)
	}

	// Should find
	versionStr = `
	Xcode 6.1
	Build version 7A220`

	version, err = findXcodeMajorVersion(versionStr)
	if err != nil {
		t.Fatalf("Failed to findMajorVersion: %s", err)
	}
	if version != 6 {
		t.Fatalf("Expected version (6), actual (%d)", version)
	}

	// Should not find
	versionStr = `
	Build version 7A220`

	version, err = findXcodeMajorVersion(versionStr)
	if err == nil {
		t.Fatalf("Should failed to findMajorVersion: %s", err)
	}
	if version != -1 {
		t.Fatalf("Expected version (-1), actual (%d)", version)
	}
}

func Test_isStringFoundInOutput(t *testing.T) {
	// Should NOT find
	searchPattern := "something"
	isShouldFind := false
	for _, anOutStr := range []string{
		"",
		"a",
		"1",
		"somethin",
		"somethinx",
		"TEST FAILED",
	} {
		if isFound := isStringFoundInOutput(searchPattern, anOutStr); isFound != isShouldFind {
			t.Logf("Search pattern was: %s", searchPattern)
			t.Logf("Input string to search in was: %s", anOutStr)
			t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
		}
	}

	// Should find
	searchPattern = "search for this"
	isShouldFind = true
	for _, anOutStr := range []string{
		"search for this",
		"-search for this",
		"search for this-",
		"-search for this-",
	} {
		if isFound := isStringFoundInOutput(searchPattern, anOutStr); isFound != isShouldFind {
			t.Logf("Search pattern was: %s", searchPattern)
			t.Logf("Input string to search in was: %s", anOutStr)
			t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
		}
	}

	// Should find - empty pattern - always "yes"
	searchPattern = ""
	isShouldFind = true
	for _, anOutStr := range []string{
		"",
		"a",
		"1",
		"TEST FAILED",
	} {
		if isFound := isStringFoundInOutput(searchPattern, anOutStr); isFound != isShouldFind {
			t.Logf("Search pattern was: %s", searchPattern)
			t.Logf("Input string to search in was: %s", anOutStr)
			t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
		}
	}
}

func Test_findTestSummaryInOutput(t *testing.T) {
	// load sample logs
	sampleIPhoneSimulatorLog, err := loadFileContent("./_samples/xcodebuild-iPhoneSimulator-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load error sample log : %s", err)
	}
	sampleUITestTimeoutLog, err := loadFileContent("./_samples/xcodebuild-UITest-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load (UITest timeout) error sample log : %s", err)
	}
	sampleOKBuildLog, err := loadFileContent("./_samples/xcodebuild-ok.txt")
	if err != nil {
		t.Fatalf("Failed to load xcodebuild-ok.txt : %s", err)
	}

	// Should NOT find
	for _, anOutStr := range []string{
		"",
		"xyz",
		"TEST SUCCEEDED",
		"TEST FAILED",
		sampleIPhoneSimulatorLog,
	} {
		testFindTestSummaryInOutput(t, false, anOutStr, true)
		testFindTestSummaryInOutput(t, false, anOutStr, false)
	}

	// should find
	testFindTestSummaryInOutput(t, true, "** TEST SUCCEEDED **", true)
	testFindTestSummaryInOutput(t, true, "** TEST FAILED **", false)
	testFindTestSummaryInOutput(t, true, "Failing tests:", false)
	testFindTestSummaryInOutput(t, true, "Testing failed:", false)
	testFindTestSummaryInOutput(t, true, sampleOKBuildLog, true)
	testFindTestSummaryInOutput(t, true, sampleUITestTimeoutLog, false)
}

func TestIsStringFoundInOutput_timeOutMessageIPhoneSimulator(t *testing.T) {
	// load sample logs
	sampleIPhoneSimulatorLog, err := loadFileContent("./_samples/xcodebuild-iPhoneSimulator-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load error sample log : %s", err)
	}
	sampleOKBuildLog, err := loadFileContent("./_samples/xcodebuild-ok.txt")
	if err != nil {
		t.Fatalf("Failed to load xcodebuild-ok.txt : %s", err)
	}

	// Should find
	for _, anOutStr := range []string{
		"iPhoneSimulator: Timed out waiting",
		"iphoneSimulator: timed out waiting",
		"iphoneSimulator: timed out waiting, test test test",
		"aaaiphoneSimulator: timed out waiting, test test test",
		"aaa iphoneSimulator: timed out waiting, test test test",
		sampleIPhoneSimulatorLog,
	} {
		testIPhoneSimulatorTimeoutWith(t, anOutStr, true)
	}

	// Should not
	for _, anOutStr := range []string{
		"",
		"iphoneSimulator:",
		sampleOKBuildLog,
	} {
		testIPhoneSimulatorTimeoutWith(t, anOutStr, false)
	}
}

func TestIsStringFoundInOutput_timeOutMessageUITest(t *testing.T) {
	// load sample logs
	sampleUITestTimeoutLog, err := loadFileContent("./_samples/xcodebuild-UITest-timeout.txt")
	if err != nil {
		t.Fatalf("Failed to load error sample log : %s", err)
	}
	sampleOKBuildLog, err := loadFileContent("./_samples/xcodebuild-ok.txt")
	if err != nil {
		t.Fatalf("Failed to load xcodebuild-ok.txt : %s", err)
	}

	// Should find
	for _, anOutStr := range []string{
		"Terminating app due to uncaught exception '_XCTestCaseInterruptionException'",
		"terminating app due to uncaught exception '_XCTestCaseInterruptionException'",
		"aaTerminating app due to uncaught exception '_XCTestCaseInterruptionException'aa",
		"aa Terminating app due to uncaught exception '_XCTestCaseInterruptionException' aa",
		sampleUITestTimeoutLog,
	} {
		testTimeOutMessageUITestWith(t, anOutStr, true)
	}

	// Should not
	for _, anOutStr := range []string{
		"",
		"Terminating app due to uncaught exception",
		"_XCTestCaseInterruptionException",
		sampleOKBuildLog,
	} {
		testTimeOutMessageUITestWith(t, anOutStr, false)
	}
}

func Test_printableCommandArgs(t *testing.T) {
	orgCmdArgs := []string{
		"xcodebuild", "-project", "MyProj.xcodeproj", "-scheme", "MyScheme",
		"build", "test",
		"-destination", "platform=iOS Simulator,name=iPhone 6,OS=latest",
		"-sdk", "iphonesimulator",
	}
	resStr := printableCommandArgs(orgCmdArgs)
	expectedStr := `xcodebuild "-project" "MyProj.xcodeproj" "-scheme" "MyScheme" "build" "test" "-destination" "platform=iOS Simulator,name=iPhone 6,OS=latest" "-sdk" "iphonesimulator"`

	if resStr != expectedStr {
		t.Log("printableCommandArgs failed to generate the expected string!")
		t.Logf(" -> expectedStr: %s", expectedStr)
		t.Logf(" -> resStr: %s", resStr)
		t.Fatalf("Expected string does not match the generated string. Original args: (%#v)", orgCmdArgs)
	}
}

func Test_findFirstDelimiter(t *testing.T) {
	test := func(strToSearchIn string, searchDelims []string, expectedDelim string, expectedIdx int) {
		foundIdx, foundDelim := findFirstDelimiter(strToSearchIn, searchDelims)

		if foundDelim != expectedDelim {
			t.Log("foundDelim != expectedDelim")
			t.Logf(" -> foundDelim: %s", foundDelim)
			t.Logf(" -> expectedDelim: %s", expectedDelim)
			t.Fatalf("searchDelims: (%#v) | strToSearchIn: %s", searchDelims, strToSearchIn)
		}
		if foundIdx != expectedIdx {
			t.Log("foundIdx != expectedIdx")
			t.Logf(" -> foundIdx: %v", foundIdx)
			t.Logf(" -> expectedIdx: %v", expectedIdx)
			t.Fatalf("searchDelims: (%#v) | strToSearchIn: %s", searchDelims, strToSearchIn)
		}
	}

	strToSearchIn := `Touch /Users/bitrise/Library/Developer/Xcode/DerivedData/BitriseSampleWithYML-duyungvabqzmagefbqehoqyodaqz/Build/Products/Debug-iphonesimulator/BitriseSampleWithYMLTests.xctest
    cd /Users/bitrise/develop/sample-apps-ios-with-bitrise-yml
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/heroku/bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/bitrise/develop/go/bin:/usr/local/opt/go/libexec/bin:/Users/bitrise/bin:/Users/bitrise/develop/go/bin:/usr/local/opt/go/libexec/bin:/Users/bitrise/bin"
    /usr/bin/touch -c /Users/bitrise/Library/Developer/Xcode/DerivedData/BitriseSampleWithYML-duyungvabqzmagefbqehoqyodaqz/Build/Products/Debug-iphonesimulator/BitriseSampleWithYMLTests.xctest

** TEST SUCCEEDED **

Test Suite 'All tests' started at 2015-09-20 16:01:13.995
Test Suite 'BitriseSampleWithYMLTests.xctest' started at 2015-09-20 16:01:13.997`

	// found
	// note: order of delimiters doesn't matter, first occurance will be used
	test(strToSearchIn, []string{"TEST SUCCEEDED", "Test Suite"}, "TEST SUCCEEDED", 842)
	test(strToSearchIn, []string{"Test Suite", "TEST SUCCEEDED"}, "TEST SUCCEEDED", 842)
	test("aaaa Test Suite aaa", []string{"Test Suite", "TEST SUCCEEDED"}, "Test Suite", 5)
	test("aaaa Test Suite aaa", []string{"TEST SUCCEEDED", "Test Suite"}, "Test Suite", 5)

	// not found
	test("aaaa", []string{"TEST SUCCEEDED", "Test Suite"}, "", -1)
}

//
// --- TESTING UTILITY FUNCS

func testIsFoundWith(t *testing.T, searchPattern, outputToSearchIn string, isShouldFind bool) {
	if isFound := isStringFoundInOutput(searchPattern, outputToSearchIn); isFound != isShouldFind {
		t.Logf("Search pattern was: %s", searchPattern)
		t.Logf("Input string to search in was: %s", outputToSearchIn)
		t.Fatalf("Expected (%v), actual (%v)", isShouldFind, isFound)
	}
}
func testIPhoneSimulatorTimeoutWith(t *testing.T, outputToSearchIn string, isShouldFind bool) {
	testIsFoundWith(t, timeOutMessageIPhoneSimulator, outputToSearchIn, isShouldFind)
}

func testTimeOutMessageUITestWith(t *testing.T, outputToSearchIn string, isShouldFind bool) {
	testIsFoundWith(t, timeOutMessageUITest, outputToSearchIn, isShouldFind)
}

func testFindTestSummaryInOutput(t *testing.T, isExpectToFind bool, fullOutput string, isRunSucess bool) {
	resStr := findTestSummaryInOutput(fullOutput, isRunSucess)
	if isExpectToFind && resStr == "" {
		t.Logf("Expected to find Test Summary in provided log.")
		debug.PrintStack()
		t.Fatalf("Provided output was: %s", fullOutput)
	}
	if !isExpectToFind && resStr != "" {
		t.Logf("Expected to NOT find Test Summary in provided log.")
		debug.PrintStack()
		t.Fatalf("Provided output was: %s", fullOutput)
	}
}

func loadFileContent(filePth string) (string, error) {
	fileBytes, err := ioutil.ReadFile(filePth)
	if err != nil {
		return "", err
	}
	return string(fileBytes), nil
}
<|endoftext|>


<commit_before>package set1

import (
	"encoding/base64"
)

// hexToDec is used to lookup a single decimal value in hex.
var hexToDec = map[byte]byte{
	'0': 0,
	'1': 1,
	'2': 2,
	'3': 3,
	'4': 4,
	'5': 5,
	'6': 6,
	'7': 7,
	'8': 8,
	'9': 9,
	'a': 10,
	'b': 11,
	'c': 12,
	'd': 13,
	'e': 14,
	'f': 15,
}

// HexDecode decodes a hexidecimal byte slice to a decimal byte slice
func HexDecode(text []byte) ([]byte, error) {
	// output is always half the size of input
	out := make([]byte, len(text)/2)

	// based on this article:
	// https://learn.sparkfun.com/tutorials/hexadecimal#converting-tofrom-decimal
	for i := 0; i < len(out); i++ {
		firstDigit := hexToDec[text[i*2]]
		secondDigit := hexToDec[text[i*2+1]]

		out[i] = firstDigit*16 + secondDigit
	}

	return out, nil
}

// HexToBase64 converts hexidecimal encoded text to base64.
func HexToBase64(text []byte) ([]byte, error) {
	raw, err := HexDecode(text)
	if err != nil {
		return []byte{}, err
	}

	b64 := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
	base64.StdEncoding.Encode(b64, raw)

	return b64, nil
}
<commit_msg>s1c1: HexDecode replace lookup table.<commit_after>package set1

import (
	"encoding/base64"
)

// hexToDec is used to lookup a single decimal value in hex.
func hexToDec(char byte) byte {
	// subtract ASCII decimal codes to convert "0" into 0 and "a" into 10.
	switch {
	case char >= '0' && char <= '9':
		return char - 48
	case char >= 'a' && char <= 'f':
		return char - 87
	}

	return 0
}

// HexDecode decodes a hexidecimal byte slice to a decimal byte slice
func HexDecode(text []byte) ([]byte, error) {
	// output is always half the size of input
	out := make([]byte, len(text)/2)

	// based on this article:
	// https://learn.sparkfun.com/tutorials/hexadecimal#converting-tofrom-decimal
	for i := 0; i < len(out); i++ {
		firstDigit := hexToDec(text[i*2])
		secondDigit := hexToDec(text[i*2+1])

		out[i] = firstDigit*16 + secondDigit
	}

	return out, nil
}

// HexToBase64 converts hexidecimal encoded text to base64.
func HexToBase64(text []byte) ([]byte, error) {
	raw, err := HexDecode(text)
	if err != nil {
		return []byte{}, err
	}

	b64 := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
	base64.StdEncoding.Encode(b64, raw)

	return b64, nil
}
<|endoftext|>


<commit_before>package softlayer

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"
	"github.com/softlayer/softlayer-go/sl"
)

func resourceSoftLayerSSHKey() *schema.Resource {
	return &schema.Resource{
		Create:   resourceSoftLayerSSHKeyCreate,
		Read:     resourceSoftLayerSSHKeyRead,
		Update:   resourceSoftLayerSSHKeyUpdate,
		Delete:   resourceSoftLayerSSHKeyDelete,
		Exists:   resourceSoftLayerSSHKeyExists,
		Importer: &schema.ResourceImporter{},

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"label": {
				Type:     schema.TypeString,
				Required: true,
			},

			"public_key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
					return strings.TrimSpace(old) == strings.TrimSpace(new)
				},
			},

			"fingerprint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"notes": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  nil,
			},
		},
	}
}

func resourceSoftLayerSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	// Build up our creation options
	opts := datatypes.Security_Ssh_Key{
		Label: sl.String(d.Get("label").(string)),
		Key:   sl.String(d.Get("public_key").(string)),
	}

	if notes, ok := d.GetOk("notes"); ok {
		opts.Notes = sl.String(notes.(string))
	}

	res, err := service.CreateObject(&opts)
	if err != nil {
		return fmt.Errorf("Error creating SSH Key: %s", err)
	}

	d.SetId(strconv.Itoa(*res.Id))
	log.Printf("[INFO] SSH Key: %d", *res.Id)

	return resourceSoftLayerSSHKeyRead(d, meta)
}

func resourceSoftLayerSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		// If the key is somehow already destroyed, mark as
		// succesfully gone
		if strings.Contains(err.Error(), "404 Not Found") {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	d.Set("id", *key.Id)
	d.Set("label", *key.Label)
	d.Set("public_key", *key.Key)
	d.Set("fingerprint", *key.Fingerprint)

	if key.Notes != nil {
		d.Set("notes", *key.Notes)
	}

	return nil
}

func resourceSoftLayerSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	if d.HasChange("label") {
		key.Label = sl.String(d.Get("label").(string))
	}

	if d.HasChange("notes") {
		key.Notes = sl.String(d.Get("notes").(string))
	}

	_, err = service.Id(keyId).EditObject(&key)
	if err != nil {
		return fmt.Errorf("Error editing SSH key: %s", err)
	}
	return nil
}

func resourceSoftLayerSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	id, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting SSH Key: %s", err)
	}

	log.Printf("[INFO] Deleting SSH key: %d", id)
	_, err = service.Id(id).DeleteObject()
	if err != nil {
		return fmt.Errorf("Error deleting SSH key: %s", err)
	}

	d.SetId("")
	return nil
}

func resourceSoftLayerSSHKeyExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, err := strconv.Atoi(d.Id())
	if err != nil {
		return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err)
	}

	result, err := service.Id(keyId).GetObject()
	return result.Id != nil && err == nil && *result.Id == keyId, nil
}
<commit_msg>Implicitly import ssh key upon resource create if key already exists<commit_after>package softlayer

import (
	"crypto/md5"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/filter"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"
	"github.com/softlayer/softlayer-go/sl"
)

func resourceSoftLayerSSHKey() *schema.Resource {
	return &schema.Resource{
		Create:   resourceSoftLayerSSHKeyCreate,
		Read:     resourceSoftLayerSSHKeyRead,
		Update:   resourceSoftLayerSSHKeyUpdate,
		Delete:   resourceSoftLayerSSHKeyDelete,
		Exists:   resourceSoftLayerSSHKeyExists,
		Importer: &schema.ResourceImporter{},

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"label": {
				Type:     schema.TypeString,
				Required: true,
			},

			"public_key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
					return strings.TrimSpace(old) == strings.TrimSpace(new)
				},
			},

			"fingerprint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"notes": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  nil,
			},
		},
	}
}

func resourceSoftLayerSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	// First check if the key exits by fingerprint
	// If so, set the Id (and fingerprint), but update notes and label (if any)
	key := d.Get("public_key").(string)
	parts := strings.Fields(key)
	if len(parts) < 2 {
		return errors.New("Invalid public key specified")
	}

	k, err := base64.StdEncoding.DecodeString(parts[1])
	if err != nil {
		return fmt.Errorf("Error decoding the public key: %s", err)
	}

	fp := md5.Sum([]byte(k))
	prints := make([]string, len(fp))
	for i, b := range fp {
		prints[i] = fmt.Sprintf("%02x", b)
	}
	fingerprint := strings.Join(prints, ":")

	keys, err := services.GetAccountService(sess).
		Filter(filter.Path("sshKeys.fingerprint").Eq(fingerprint).Build()).
		GetSshKeys()
	if err == nil && len(keys) > 0 {
		slKey := keys[0]
		id := *slKey.Id
		slKey.Id = nil
		d.SetId(fmt.Sprintf("%d", id))
		d.Set("fingerprint", fingerprint)
		editKey := false

		notes := d.Get("notes").(string)
		if notes != "" && (slKey.Notes == nil || notes != *slKey.Notes) {
			slKey.Notes = sl.String(notes)
			editKey = true
		} else if slKey.Notes != nil {
			d.Set("notes", *slKey.Notes)
		}

		label := d.Get("label").(string)
		if label != *slKey.Label {
			slKey.Label = sl.String(d.Get("label").(string))
			editKey = true
		}

		if editKey {
			_, err = service.Id(id).EditObject(&slKey)
			return err
		}

		return nil
	} // End of "Import"

	// Build up our creation options
	opts := datatypes.Security_Ssh_Key{
		Label: sl.String(d.Get("label").(string)),
		Key:   sl.String(d.Get("public_key").(string)),
	}

	if notes, ok := d.GetOk("notes"); ok {
		opts.Notes = sl.String(notes.(string))
	}

	res, err := service.CreateObject(&opts)
	if err != nil {
		return fmt.Errorf("Error creating SSH Key: %s", err)
	}

	d.SetId(strconv.Itoa(*res.Id))
	log.Printf("[INFO] SSH Key: %d", *res.Id)

	return resourceSoftLayerSSHKeyRead(d, meta)
}

func resourceSoftLayerSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		// If the key is somehow already destroyed, mark as
		// succesfully gone
		if strings.Contains(err.Error(), "404 Not Found") {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	d.Set("id", *key.Id)
	d.Set("label", *key.Label)
	d.Set("public_key", *key.Key)
	d.Set("fingerprint", *key.Fingerprint)

	if key.Notes != nil {
		d.Set("notes", *key.Notes)
	}

	return nil
}

func resourceSoftLayerSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	if d.HasChange("label") {
		key.Label = sl.String(d.Get("label").(string))
	}

	if d.HasChange("notes") {
		key.Notes = sl.String(d.Get("notes").(string))
	}

	_, err = service.Id(keyId).EditObject(&key)
	if err != nil {
		return fmt.Errorf("Error editing SSH key: %s", err)
	}
	return nil
}

func resourceSoftLayerSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	id, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting SSH Key: %s", err)
	}

	log.Printf("[INFO] Deleting SSH key: %d", id)
	_, err = service.Id(id).DeleteObject()
	if err != nil {
		return fmt.Errorf("Error deleting SSH key: %s", err)
	}

	d.SetId("")
	return nil
}

func resourceSoftLayerSSHKeyExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, err := strconv.Atoi(d.Id())
	if err != nil {
		return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err)
	}

	result, err := service.Id(keyId).GetObject()
	return result.Id != nil && err == nil && *result.Id == keyId, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/textproto"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
)

var (
	repoDir      string
	largeObjects = newLfsStorage()
	server       *httptest.Server
	serveBatch   = true
)

func main() {
	repoDir = os.Getenv("LFSTEST_DIR")

	mux := http.NewServeMux()
	server = httptest.NewServer(mux)
	stopch := make(chan bool)

	mux.HandleFunc("/startbatch", func(w http.ResponseWriter, r *http.Request) {
		serveBatch = true
	})

	mux.HandleFunc("/stopbatch", func(w http.ResponseWriter, r *http.Request) {
		serveBatch = false
	})

	mux.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) {
		stopch <- true
	})

	mux.HandleFunc("/storage/", storageHandler)
	mux.HandleFunc("/redirect307/", redirect307Handler)
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if strings.Contains(r.URL.Path, "/info/lfs") {
			if skipIfBadAuth(w, r) {
				return
			}

			lfsHandler(w, r)
			return
		}

		log.Printf("git http-backend %s %s\n", r.Method, r.URL)
		gitHandler(w, r)
	})

	urlname := os.Getenv("LFSTEST_URL")
	if len(urlname) == 0 {
		urlname = "lfstest-gitserver"
	}

	file, err := os.Create(urlname)
	if err != nil {
		log.Fatalln(err)
	}

	file.Write([]byte(server.URL))
	file.Close()
	log.Println(server.URL)

	defer func() {
		os.RemoveAll(urlname)
	}()

	<-stopch
	log.Println("git server done")
}

type lfsObject struct {
	Oid   string             `json:"oid,omitempty"`
	Size  int64              `json:"size,omitempty"`
	Links map[string]lfsLink `json:"_links,omitempty"`
}

type lfsLink struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

// handles any requests with "{name}.server.git/info/lfs" in the path
func lfsHandler(w http.ResponseWriter, r *http.Request) {
	repo, err := repoFromLfsUrl(r.URL.Path)
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
		return
	}

	log.Printf("git lfs %s %s repo: %s\n", r.Method, r.URL, repo)
	w.Header().Set("Content-Type", "application/vnd.git-lfs+json")
	switch r.Method {
	case "POST":
		if strings.HasSuffix(r.URL.String(), "batch") {
			lfsBatchHandler(w, r, repo)
		} else {
			lfsPostHandler(w, r, repo)
		}
	case "GET":
		lfsGetHandler(w, r, repo)
	default:
		w.WriteHeader(405)
	}
}

func lfsUrl(repo, oid string) string {
	return server.URL + "/storage/" + oid + "?r=" + repo
}

func lfsPostHandler(w http.ResponseWriter, r *http.Request, repo string) {
	buf := &bytes.Buffer{}
	tee := io.TeeReader(r.Body, buf)
	obj := &lfsObject{}
	err := json.NewDecoder(tee).Decode(obj)
	io.Copy(ioutil.Discard, r.Body)
	r.Body.Close()

	log.Println("REQUEST")
	log.Println(buf.String())
	log.Printf("OID: %s\n", obj.Oid)
	log.Printf("Size: %d\n", obj.Size)

	if err != nil {
		log.Fatal(err)
	}

	res := &lfsObject{
		Oid:  obj.Oid,
		Size: obj.Size,
		Links: map[string]lfsLink{
			"upload": lfsLink{
				Href:   lfsUrl(repo, obj.Oid),
				Header: map[string]string{},
			},
		},
	}

	if testingChunkedTransferEncoding(r) {
		res.Links["upload"].Header["Transfer-Encoding"] = "chunked"
	}

	by, err := json.Marshal(res)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("RESPONSE: 202")
	log.Println(string(by))

	w.WriteHeader(202)
	w.Write(by)
}

func lfsGetHandler(w http.ResponseWriter, r *http.Request, repo string) {
	parts := strings.Split(r.URL.Path, "/")
	oid := parts[len(parts)-1]

	by, ok := largeObjects.Get(repo, oid)
	if !ok {
		w.WriteHeader(404)
		return
	}

	obj := &lfsObject{
		Oid:  oid,
		Size: int64(len(by)),
		Links: map[string]lfsLink{
			"download": lfsLink{
				Href: lfsUrl(repo, oid),
			},
		},
	}

	by, err := json.Marshal(obj)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("RESPONSE: 200")
	log.Println(string(by))

	w.WriteHeader(200)
	w.Write(by)
}

func lfsBatchHandler(w http.ResponseWriter, r *http.Request, repo string) {
	if !serveBatch {
		w.WriteHeader(404)
		return
	}

	type batchReq struct {
		Operation string      `json:"operation"`
		Objects   []lfsObject `json:"objects"`
	}

	buf := &bytes.Buffer{}
	tee := io.TeeReader(r.Body, buf)
	var objs batchReq
	err := json.NewDecoder(tee).Decode(&objs)
	io.Copy(ioutil.Discard, r.Body)
	r.Body.Close()

	log.Println("REQUEST")
	log.Println(buf.String())

	if err != nil {
		log.Fatal(err)
	}

	res := []lfsObject{}
	testingChunked := testingChunkedTransferEncoding(r)
	for _, obj := range objs.Objects {
		o := lfsObject{
			Oid:  obj.Oid,
			Size: obj.Size,
			Links: map[string]lfsLink{
				"upload": lfsLink{
					Href:   lfsUrl(repo, obj.Oid),
					Header: map[string]string{},
				},
			},
		}

		if testingChunked {
			o.Links["upload"].Header["Transfer-Encoding"] = "chunked"
		}

		res = append(res, o)
	}

	ores := map[string][]lfsObject{"objects": res}

	by, err := json.Marshal(ores)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("RESPONSE: 200")
	log.Println(string(by))

	w.WriteHeader(200)
	w.Write(by)
}

// handles any /storage/{oid} requests
func storageHandler(w http.ResponseWriter, r *http.Request) {
	repo := r.URL.Query().Get("r")
	log.Printf("storage %s %s repo: %s\n", r.Method, r.URL, repo)
	switch r.Method {
	case "PUT":
		if testingChunkedTransferEncoding(r) {
			valid := false
			for _, value := range r.TransferEncoding {
				if value == "chunked" {
					valid = true
					break
				}
			}
			if !valid {
				log.Fatal("Chunked transfer encoding expected")
			}
		}

		hash := sha256.New()
		buf := &bytes.Buffer{}
		io.Copy(io.MultiWriter(hash, buf), r.Body)
		oid := hex.EncodeToString(hash.Sum(nil))
		if !strings.HasSuffix(r.URL.Path, "/"+oid) {
			w.WriteHeader(403)
			return
		}

		largeObjects.Set(repo, oid, buf.Bytes())

	case "GET":
		parts := strings.Split(r.URL.Path, "/")
		oid := parts[len(parts)-1]

		if by, ok := largeObjects.Get(repo, oid); ok {
			w.Write(by)
			return
		}

		w.WriteHeader(404)
	default:
		w.WriteHeader(405)
	}
}

func gitHandler(w http.ResponseWriter, r *http.Request) {
	defer func() {
		io.Copy(ioutil.Discard, r.Body)
		r.Body.Close()
	}()

	cmd := exec.Command("git", "http-backend")
	cmd.Env = []string{
		fmt.Sprintf("GIT_PROJECT_ROOT=%s", repoDir),
		fmt.Sprintf("GIT_HTTP_EXPORT_ALL="),
		fmt.Sprintf("PATH_INFO=%s", r.URL.Path),
		fmt.Sprintf("QUERY_STRING=%s", r.URL.RawQuery),
		fmt.Sprintf("REQUEST_METHOD=%s", r.Method),
		fmt.Sprintf("CONTENT_TYPE=%s", r.Header.Get("Content-Type")),
	}

	buffer := &bytes.Buffer{}
	cmd.Stdin = r.Body
	cmd.Stdout = buffer
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}

	text := textproto.NewReader(bufio.NewReader(buffer))

	code, _, _ := text.ReadCodeLine(-1)

	if code != 0 {
		w.WriteHeader(code)
	}

	headers, _ := text.ReadMIMEHeader()
	head := w.Header()
	for key, values := range headers {
		for _, value := range values {
			head.Add(key, value)
		}
	}

	io.Copy(w, text.R)
}

func redirect307Handler(w http.ResponseWriter, r *http.Request) {
	// Send a redirect to info/lfs
	// Make it either absolute or relative depending on subpath
	parts := strings.Split(r.URL.Path, "/")
	// first element is always blank since rooted
	var redirectTo string
	if parts[2] == "rel" {
		redirectTo = "/" + strings.Join(parts[3:], "/")
	} else if parts[2] == "abs" {
		redirectTo = server.URL + "/" + strings.Join(parts[3:], "/")
	} else {
		log.Fatal(fmt.Errorf("Invalid URL for redirect: %v", r.URL))
		w.WriteHeader(404)
		return
	}
	w.Header().Set("Location", redirectTo)
	w.WriteHeader(307)
}

func testingChunkedTransferEncoding(r *http.Request) bool {
	return strings.HasPrefix(r.URL.String(), "/test-chunked-transfer-encoding")
}

var lfsUrlRE = regexp.MustCompile(`\A/?([^/]+)/info/lfs`)

func repoFromLfsUrl(urlpath string) (string, error) {
	matches := lfsUrlRE.FindStringSubmatch(urlpath)
	if len(matches) != 2 {
		return "", fmt.Errorf("LFS url '%s' does not match %v", urlpath, lfsUrlRE)
	}

	repo := matches[1]
	if strings.HasSuffix(repo, ".git") {
		return repo[0 : len(repo)-4], nil
	}
	return repo, nil
}

type lfsStorage struct {
	objects map[string]map[string][]byte
	mutex   *sync.Mutex
}

func (s *lfsStorage) Get(repo, oid string) ([]byte, bool) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	repoObjects, ok := s.objects[repo]
	if !ok {
		return nil, ok
	}

	by, ok := repoObjects[oid]
	return by, ok
}

func (s *lfsStorage) Set(repo, oid string, by []byte) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	repoObjects, ok := s.objects[repo]
	if !ok {
		repoObjects = make(map[string][]byte)
		s.objects[repo] = repoObjects
	}
	repoObjects[oid] = by
}

func newLfsStorage() *lfsStorage {
	return &lfsStorage{
		objects: make(map[string]map[string][]byte),
		mutex:   &sync.Mutex{},
	}
}

func skipIfBadAuth(w http.ResponseWriter, r *http.Request) bool {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		w.WriteHeader(401)
		return true
	}

	if strings.HasPrefix(auth, "Basic ") {
		decodeBy, err := base64.StdEncoding.DecodeString(auth[6:len(auth)])
		decoded := string(decodeBy)
		if err != nil {
			w.WriteHeader(403)
			log.Printf("Error decoding auth: %s\n", err)
			return true
		}

		parts := strings.SplitN(decoded, ":", 2)
		if len(parts) == 2 {
			switch parts[0] {
			case "user":
				if parts[1] == "pass" {
					return false
				}
			case "path":
				if strings.HasPrefix(r.URL.Path, "/"+parts[1]+"/") {
					return false
				}
			}
		}
	}

	w.WriteHeader(403)
	log.Printf("Bad auth: %q\n", auth)
	return true
}
<commit_msg>fewer loc<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/textproto"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
)

var (
	repoDir      string
	largeObjects = newLfsStorage()
	server       *httptest.Server
	serveBatch   = true
)

func main() {
	repoDir = os.Getenv("LFSTEST_DIR")

	mux := http.NewServeMux()
	server = httptest.NewServer(mux)
	stopch := make(chan bool)

	mux.HandleFunc("/startbatch", func(w http.ResponseWriter, r *http.Request) {
		serveBatch = true
	})

	mux.HandleFunc("/stopbatch", func(w http.ResponseWriter, r *http.Request) {
		serveBatch = false
	})

	mux.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) {
		stopch <- true
	})

	mux.HandleFunc("/storage/", storageHandler)
	mux.HandleFunc("/redirect307/", redirect307Handler)
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if strings.Contains(r.URL.Path, "/info/lfs") {
			if !skipIfBadAuth(w, r) {
				lfsHandler(w, r)
			}

			return
		}

		log.Printf("git http-backend %s %s\n", r.Method, r.URL)
		gitHandler(w, r)
	})

	urlname := os.Getenv("LFSTEST_URL")
	if len(urlname) == 0 {
		urlname = "lfstest-gitserver"
	}

	file, err := os.Create(urlname)
	if err != nil {
		log.Fatalln(err)
	}

	file.Write([]byte(server.URL))
	file.Close()
	log.Println(server.URL)

	defer func() {
		os.RemoveAll(urlname)
	}()

	<-stopch
	log.Println("git server done")
}

type lfsObject struct {
	Oid   string             `json:"oid,omitempty"`
	Size  int64              `json:"size,omitempty"`
	Links map[string]lfsLink `json:"_links,omitempty"`
}

type lfsLink struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

// handles any requests with "{name}.server.git/info/lfs" in the path
func lfsHandler(w http.ResponseWriter, r *http.Request) {
	repo, err := repoFromLfsUrl(r.URL.Path)
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
		return
	}

	log.Printf("git lfs %s %s repo: %s\n", r.Method, r.URL, repo)
	w.Header().Set("Content-Type", "application/vnd.git-lfs+json")
	switch r.Method {
	case "POST":
		if strings.HasSuffix(r.URL.String(), "batch") {
			lfsBatchHandler(w, r, repo)
		} else {
			lfsPostHandler(w, r, repo)
		}
	case "GET":
		lfsGetHandler(w, r, repo)
	default:
		w.WriteHeader(405)
	}
}

func lfsUrl(repo, oid string) string {
	return server.URL + "/storage/" + oid + "?r=" + repo
}

func lfsPostHandler(w http.ResponseWriter, r *http.Request, repo string) {
	buf := &bytes.Buffer{}
	tee := io.TeeReader(r.Body, buf)
	obj := &lfsObject{}
	err := json.NewDecoder(tee).Decode(obj)
	io.Copy(ioutil.Discard, r.Body)
	r.Body.Close()

	log.Println("REQUEST")
	log.Println(buf.String())
	log.Printf("OID: %s\n", obj.Oid)
	log.Printf("Size: %d\n", obj.Size)

	if err != nil {
		log.Fatal(err)
	}

	res := &lfsObject{
		Oid:  obj.Oid,
		Size: obj.Size,
		Links: map[string]lfsLink{
			"upload": lfsLink{
				Href:   lfsUrl(repo, obj.Oid),
				Header: map[string]string{},
			},
		},
	}

	if testingChunkedTransferEncoding(r) {
		res.Links["upload"].Header["Transfer-Encoding"] = "chunked"
	}

	by, err := json.Marshal(res)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("RESPONSE: 202")
	log.Println(string(by))

	w.WriteHeader(202)
	w.Write(by)
}

func lfsGetHandler(w http.ResponseWriter, r *http.Request, repo string) {
	parts := strings.Split(r.URL.Path, "/")
	oid := parts[len(parts)-1]

	by, ok := largeObjects.Get(repo, oid)
	if !ok {
		w.WriteHeader(404)
		return
	}

	obj := &lfsObject{
		Oid:  oid,
		Size: int64(len(by)),
		Links: map[string]lfsLink{
			"download": lfsLink{
				Href: lfsUrl(repo, oid),
			},
		},
	}

	by, err := json.Marshal(obj)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("RESPONSE: 200")
	log.Println(string(by))

	w.WriteHeader(200)
	w.Write(by)
}

func lfsBatchHandler(w http.ResponseWriter, r *http.Request, repo string) {
	if !serveBatch {
		w.WriteHeader(404)
		return
	}

	type batchReq struct {
		Operation string      `json:"operation"`
		Objects   []lfsObject `json:"objects"`
	}

	buf := &bytes.Buffer{}
	tee := io.TeeReader(r.Body, buf)
	var objs batchReq
	err := json.NewDecoder(tee).Decode(&objs)
	io.Copy(ioutil.Discard, r.Body)
	r.Body.Close()

	log.Println("REQUEST")
	log.Println(buf.String())

	if err != nil {
		log.Fatal(err)
	}

	res := []lfsObject{}
	testingChunked := testingChunkedTransferEncoding(r)
	for _, obj := range objs.Objects {
		o := lfsObject{
			Oid:  obj.Oid,
			Size: obj.Size,
			Links: map[string]lfsLink{
				"upload": lfsLink{
					Href:   lfsUrl(repo, obj.Oid),
					Header: map[string]string{},
				},
			},
		}

		if testingChunked {
			o.Links["upload"].Header["Transfer-Encoding"] = "chunked"
		}

		res = append(res, o)
	}

	ores := map[string][]lfsObject{"objects": res}

	by, err := json.Marshal(ores)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("RESPONSE: 200")
	log.Println(string(by))

	w.WriteHeader(200)
	w.Write(by)
}

// handles any /storage/{oid} requests
func storageHandler(w http.ResponseWriter, r *http.Request) {
	repo := r.URL.Query().Get("r")
	log.Printf("storage %s %s repo: %s\n", r.Method, r.URL, repo)
	switch r.Method {
	case "PUT":
		if testingChunkedTransferEncoding(r) {
			valid := false
			for _, value := range r.TransferEncoding {
				if value == "chunked" {
					valid = true
					break
				}
			}
			if !valid {
				log.Fatal("Chunked transfer encoding expected")
			}
		}

		hash := sha256.New()
		buf := &bytes.Buffer{}
		io.Copy(io.MultiWriter(hash, buf), r.Body)
		oid := hex.EncodeToString(hash.Sum(nil))
		if !strings.HasSuffix(r.URL.Path, "/"+oid) {
			w.WriteHeader(403)
			return
		}

		largeObjects.Set(repo, oid, buf.Bytes())

	case "GET":
		parts := strings.Split(r.URL.Path, "/")
		oid := parts[len(parts)-1]

		if by, ok := largeObjects.Get(repo, oid); ok {
			w.Write(by)
			return
		}

		w.WriteHeader(404)
	default:
		w.WriteHeader(405)
	}
}

func gitHandler(w http.ResponseWriter, r *http.Request) {
	defer func() {
		io.Copy(ioutil.Discard, r.Body)
		r.Body.Close()
	}()

	cmd := exec.Command("git", "http-backend")
	cmd.Env = []string{
		fmt.Sprintf("GIT_PROJECT_ROOT=%s", repoDir),
		fmt.Sprintf("GIT_HTTP_EXPORT_ALL="),
		fmt.Sprintf("PATH_INFO=%s", r.URL.Path),
		fmt.Sprintf("QUERY_STRING=%s", r.URL.RawQuery),
		fmt.Sprintf("REQUEST_METHOD=%s", r.Method),
		fmt.Sprintf("CONTENT_TYPE=%s", r.Header.Get("Content-Type")),
	}

	buffer := &bytes.Buffer{}
	cmd.Stdin = r.Body
	cmd.Stdout = buffer
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}

	text := textproto.NewReader(bufio.NewReader(buffer))

	code, _, _ := text.ReadCodeLine(-1)

	if code != 0 {
		w.WriteHeader(code)
	}

	headers, _ := text.ReadMIMEHeader()
	head := w.Header()
	for key, values := range headers {
		for _, value := range values {
			head.Add(key, value)
		}
	}

	io.Copy(w, text.R)
}

func redirect307Handler(w http.ResponseWriter, r *http.Request) {
	// Send a redirect to info/lfs
	// Make it either absolute or relative depending on subpath
	parts := strings.Split(r.URL.Path, "/")
	// first element is always blank since rooted
	var redirectTo string
	if parts[2] == "rel" {
		redirectTo = "/" + strings.Join(parts[3:], "/")
	} else if parts[2] == "abs" {
		redirectTo = server.URL + "/" + strings.Join(parts[3:], "/")
	} else {
		log.Fatal(fmt.Errorf("Invalid URL for redirect: %v", r.URL))
		w.WriteHeader(404)
		return
	}
	w.Header().Set("Location", redirectTo)
	w.WriteHeader(307)
}

func testingChunkedTransferEncoding(r *http.Request) bool {
	return strings.HasPrefix(r.URL.String(), "/test-chunked-transfer-encoding")
}

var lfsUrlRE = regexp.MustCompile(`\A/?([^/]+)/info/lfs`)

func repoFromLfsUrl(urlpath string) (string, error) {
	matches := lfsUrlRE.FindStringSubmatch(urlpath)
	if len(matches) != 2 {
		return "", fmt.Errorf("LFS url '%s' does not match %v", urlpath, lfsUrlRE)
	}

	repo := matches[1]
	if strings.HasSuffix(repo, ".git") {
		return repo[0 : len(repo)-4], nil
	}
	return repo, nil
}

type lfsStorage struct {
	objects map[string]map[string][]byte
	mutex   *sync.Mutex
}

func (s *lfsStorage) Get(repo, oid string) ([]byte, bool) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	repoObjects, ok := s.objects[repo]
	if !ok {
		return nil, ok
	}

	by, ok := repoObjects[oid]
	return by, ok
}

func (s *lfsStorage) Set(repo, oid string, by []byte) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	repoObjects, ok := s.objects[repo]
	if !ok {
		repoObjects = make(map[string][]byte)
		s.objects[repo] = repoObjects
	}
	repoObjects[oid] = by
}

func newLfsStorage() *lfsStorage {
	return &lfsStorage{
		objects: make(map[string]map[string][]byte),
		mutex:   &sync.Mutex{},
	}
}

func skipIfBadAuth(w http.ResponseWriter, r *http.Request) bool {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		w.WriteHeader(401)
		return true
	}

	if strings.HasPrefix(auth, "Basic ") {
		decodeBy, err := base64.StdEncoding.DecodeString(auth[6:len(auth)])
		decoded := string(decodeBy)
		if err != nil {
			w.WriteHeader(403)
			log.Printf("Error decoding auth: %s\n", err)
			return true
		}

		parts := strings.SplitN(decoded, ":", 2)
		if len(parts) == 2 {
			switch parts[0] {
			case "user":
				if parts[1] == "pass" {
					return false
				}
			case "path":
				if strings.HasPrefix(r.URL.Path, "/"+parts[1]+"/") {
					return false
				}
			}
		}
	}

	w.WriteHeader(403)
	log.Printf("Bad auth: %q\n", auth)
	return true
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package tikv

import (
	"context"
	"fmt"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/errors"
	"github.com/pingcap/failpoint"
	pb "github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/util/logutil"
	"go.uber.org/zap"
)

type testSnapshotSuite struct {
	OneByOneSuite
	store   *tikvStore
	prefix  string
	rowNums []int
}

var _ = Suite(&testSnapshotSuite{})

func (s *testSnapshotSuite) SetUpSuite(c *C) {
	s.OneByOneSuite.SetUpSuite(c)
	s.store = NewTestStore(c).(*tikvStore)
	s.prefix = fmt.Sprintf("snapshot_%d", time.Now().Unix())
	s.rowNums = append(s.rowNums, 1, 100, 191)
}

func (s *testSnapshotSuite) TearDownSuite(c *C) {
	txn := s.beginTxn(c)
	scanner, err := txn.Iter(encodeKey(s.prefix, ""), nil)
	c.Assert(err, IsNil)
	c.Assert(scanner, NotNil)
	for scanner.Valid() {
		k := scanner.Key()
		err = txn.Delete(k)
		c.Assert(err, IsNil)
		scanner.Next()
	}
	err = txn.Commit(context.Background())
	c.Assert(err, IsNil)
	err = s.store.Close()
	c.Assert(err, IsNil)
	s.OneByOneSuite.TearDownSuite(c)
}

func (s *testSnapshotSuite) beginTxn(c *C) *tikvTxn {
	txn, err := s.store.Begin()
	c.Assert(err, IsNil)
	return txn.(*tikvTxn)
}

func (s *testSnapshotSuite) checkAll(keys []kv.Key, c *C) {
	txn := s.beginTxn(c)
	snapshot := newTiKVSnapshot(s.store, kv.Version{Ver: txn.StartTS()}, 0)
	m, err := snapshot.BatchGet(context.Background(), keys)
	c.Assert(err, IsNil)

	scan, err := txn.Iter(encodeKey(s.prefix, ""), nil)
	c.Assert(err, IsNil)
	cnt := 0
	for scan.Valid() {
		cnt++
		k := scan.Key()
		v := scan.Value()
		v2, ok := m[string(k)]
		c.Assert(ok, IsTrue, Commentf("key: %q", k))
		c.Assert(v, BytesEquals, v2)
		scan.Next()
	}
	err = txn.Commit(context.Background())
	c.Assert(err, IsNil)
	c.Assert(m, HasLen, cnt)
}

func (s *testSnapshotSuite) deleteKeys(keys []kv.Key, c *C) {
	txn := s.beginTxn(c)
	for _, k := range keys {
		err := txn.Delete(k)
		c.Assert(err, IsNil)
	}
	err := txn.Commit(context.Background())
	c.Assert(err, IsNil)
}

func (s *testSnapshotSuite) TestBatchGet(c *C) {
	for _, rowNum := range s.rowNums {
		logutil.BgLogger().Debug("test BatchGet",
			zap.Int("length", rowNum))
		txn := s.beginTxn(c)
		for i := 0; i < rowNum; i++ {
			k := encodeKey(s.prefix, s08d("key", i))
			err := txn.Set(k, valueBytes(i))
			c.Assert(err, IsNil)
		}
		err := txn.Commit(context.Background())
		c.Assert(err, IsNil)

		keys := makeKeys(rowNum, s.prefix)
		s.checkAll(keys, c)
		s.deleteKeys(keys, c)
	}
}

type contextKey string

func (s *testSnapshotSuite) TestSnapshotCache(c *C) {
	txn := s.beginTxn(c)
	c.Assert(txn.Set(kv.Key("x"), []byte("x")), IsNil)
	c.Assert(txn.Delete(kv.Key("y")), IsNil) // store data is affected by other tests.
	c.Assert(txn.Commit(context.Background()), IsNil)

	txn = s.beginTxn(c)
	snapshot := newTiKVSnapshot(s.store, kv.Version{Ver: txn.StartTS()}, 0)
	_, err := snapshot.BatchGet(context.Background(), []kv.Key{kv.Key("x"), kv.Key("y")})
	c.Assert(err, IsNil)

	c.Assert(failpoint.Enable("github.com/pingcap/tidb/store/tikv/snapshot-get-cache-fail", `return(true)`), IsNil)
	ctx := context.WithValue(context.Background(), contextKey("TestSnapshotCache"), true)
	_, err = snapshot.Get(ctx, kv.Key("x"))
	c.Assert(err, IsNil)

	_, err = snapshot.Get(ctx, kv.Key("y"))
	c.Assert(kv.IsErrNotFound(err), IsTrue)

	c.Assert(failpoint.Disable("github.com/pingcap/tidb/store/tikv/snapshot-get-cache-fail"), IsNil)
}

func (s *testSnapshotSuite) TestBatchGetNotExist(c *C) {
	for _, rowNum := range s.rowNums {
		logutil.BgLogger().Debug("test BatchGetNotExist",
			zap.Int("length", rowNum))
		txn := s.beginTxn(c)
		for i := 0; i < rowNum; i++ {
			k := encodeKey(s.prefix, s08d("key", i))
			err := txn.Set(k, valueBytes(i))
			c.Assert(err, IsNil)
		}
		err := txn.Commit(context.Background())
		c.Assert(err, IsNil)

		keys := makeKeys(rowNum, s.prefix)
		keys = append(keys, kv.Key("noSuchKey"))
		s.checkAll(keys, c)
		s.deleteKeys(keys, c)
	}
}

func makeKeys(rowNum int, prefix string) []kv.Key {
	keys := make([]kv.Key, 0, rowNum)
	for i := 0; i < rowNum; i++ {
		k := encodeKey(prefix, s08d("key", i))
		keys = append(keys, k)
	}
	return keys
}

func (s *testSnapshotSuite) TestWriteConflictPrettyFormat(c *C) {
	conflict := &pb.WriteConflict{
		StartTs:          399402937522847774,
		ConflictTs:       399402937719455772,
		ConflictCommitTs: 399402937719455773,
		Key:              []byte{116, 128, 0, 0, 0, 0, 0, 1, 155, 95, 105, 128, 0, 0, 0, 0, 0, 0, 1, 1, 82, 87, 48, 49, 0, 0, 0, 0, 251, 1, 55, 54, 56, 50, 50, 49, 49, 48, 255, 57, 0, 0, 0, 0, 0, 0, 0, 248, 1, 0, 0, 0, 0, 0, 0, 0, 0, 247},
		Primary:          []byte{116, 128, 0, 0, 0, 0, 0, 1, 155, 95, 105, 128, 0, 0, 0, 0, 0, 0, 1, 1, 82, 87, 48, 49, 0, 0, 0, 0, 251, 1, 55, 54, 56, 50, 50, 49, 49, 48, 255, 57, 0, 0, 0, 0, 0, 0, 0, 248, 1, 0, 0, 0, 0, 0, 0, 0, 0, 247},
	}

	expectedStr := "[kv:9007]Write conflict, " +
		"txnStartTS=399402937522847774, conflictStartTS=399402937719455772, conflictCommitTS=399402937719455773, " +
		"key={tableID=411, indexID=1, indexValues={RW01, 768221109, , }} " +
		"primary={tableID=411, indexID=1, indexValues={RW01, 768221109, , }} " +
		kv.TxnRetryableMark
	c.Assert(newWriteConflictError(conflict).Error(), Equals, expectedStr)

	conflict = &pb.WriteConflict{
		StartTs:          399402937522847774,
		ConflictTs:       399402937719455772,
		ConflictCommitTs: 399402937719455773,
		Key:              []byte{0x6d, 0x44, 0x42, 0x3a, 0x35, 0x36, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x54, 0x49, 0x44, 0x3a, 0x31, 0x30, 0x38, 0x0, 0xfe},
		Primary:          []byte{0x6d, 0x44, 0x42, 0x3a, 0x35, 0x36, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x54, 0x49, 0x44, 0x3a, 0x31, 0x30, 0x38, 0x0, 0xfe},
	}
	expectedStr = "[kv:9007]Write conflict, " +
		"txnStartTS=399402937522847774, conflictStartTS=399402937719455772, conflictCommitTS=399402937719455773, " +
		"key={metaKey=true, key=DB:56, field=TID:108} " +
		"primary={metaKey=true, key=DB:56, field=TID:108} " +
		kv.TxnRetryableMark
	c.Assert(newWriteConflictError(conflict).Error(), Equals, expectedStr)
}

func (s *testSnapshotSuite) TestLockNotFoundPrint(c *C) {
	msg := "Txn(Mvcc(TxnLockNotFound { start_ts: 408090278408224772, commit_ts: 408090279311835140, " +
		"key: [116, 128, 0, 0, 0, 0, 0, 50, 137, 95, 105, 128, 0, 0, 0, 0,0 ,0, 1, 1, 67, 49, 57, 48, 57, 50, 57, 48, 255, 48, 48, 48, 48, 48, 52, 56, 54, 255, 50, 53, 53, 50, 51, 0, 0, 0, 252] }))"
	key := prettyLockNotFoundKey(msg)
	c.Assert(key, Equals, "{tableID=12937, indexID=1, indexValues={C19092900000048625523, }}")
}

func (s *testSnapshotSuite) TestSkipLargeTxnLock(c *C) {
	x := kv.Key("x_key_TestSkipLargeTxnLock")
	y := kv.Key("y_key_TestSkipLargeTxnLock")
	txn := s.beginTxn(c)
	c.Assert(txn.Set(x, []byte("x")), IsNil)
	c.Assert(txn.Set(y, []byte("y")), IsNil)
	ctx := context.Background()
	bo := NewBackoffer(ctx, PrewriteMaxBackoff)
	committer, err := newTwoPhaseCommitterWithInit(txn, 0)
	c.Assert(err, IsNil)
	committer.lockTTL = txnLockTTL(txn.startTime, 10<<20)
	c.Assert(committer.prewriteKeys(bo, committer.keys), IsNil)

	txn1 := s.beginTxn(c)
	// txn1 is not blocked by txn in the large txn protocol.
	_, err = txn1.Get(ctx, kv.Key(x))
	c.Assert(kv.IsErrNotFound(errors.Trace(err)), IsTrue)

	res, err := txn1.BatchGet(ctx, []kv.Key{x, y, kv.Key("z")})
	c.Assert(err, IsNil)
	c.Assert(res, HasLen, 0)

	// Commit txn, check the final commit ts is pushed.
	committer.commitTS = txn.StartTS() + 1
	c.Assert(committer.commitKeys(bo, committer.keys), IsNil)
	status, err := s.store.lockResolver.GetTxnStatus(txn.StartTS(), 0, x)
	c.Assert(err, IsNil)
	c.Assert(status.IsCommitted(), IsTrue)
	c.Assert(status.CommitTS(), Greater, txn1.StartTS())
}
<commit_msg>case: make TestSkipLargeTxnLock more stable (#14879)<commit_after>// Copyright 2016 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package tikv

import (
	"context"
	"fmt"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/errors"
	"github.com/pingcap/failpoint"
	pb "github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/util/logutil"
	"go.uber.org/zap"
)

type testSnapshotSuite struct {
	OneByOneSuite
	store   *tikvStore
	prefix  string
	rowNums []int
}

var _ = Suite(&testSnapshotSuite{})

func (s *testSnapshotSuite) SetUpSuite(c *C) {
	s.OneByOneSuite.SetUpSuite(c)
	s.store = NewTestStore(c).(*tikvStore)
	s.prefix = fmt.Sprintf("snapshot_%d", time.Now().Unix())
	s.rowNums = append(s.rowNums, 1, 100, 191)
}

func (s *testSnapshotSuite) TearDownSuite(c *C) {
	txn := s.beginTxn(c)
	scanner, err := txn.Iter(encodeKey(s.prefix, ""), nil)
	c.Assert(err, IsNil)
	c.Assert(scanner, NotNil)
	for scanner.Valid() {
		k := scanner.Key()
		err = txn.Delete(k)
		c.Assert(err, IsNil)
		scanner.Next()
	}
	err = txn.Commit(context.Background())
	c.Assert(err, IsNil)
	err = s.store.Close()
	c.Assert(err, IsNil)
	s.OneByOneSuite.TearDownSuite(c)
}

func (s *testSnapshotSuite) beginTxn(c *C) *tikvTxn {
	txn, err := s.store.Begin()
	c.Assert(err, IsNil)
	return txn.(*tikvTxn)
}

func (s *testSnapshotSuite) checkAll(keys []kv.Key, c *C) {
	txn := s.beginTxn(c)
	snapshot := newTiKVSnapshot(s.store, kv.Version{Ver: txn.StartTS()}, 0)
	m, err := snapshot.BatchGet(context.Background(), keys)
	c.Assert(err, IsNil)

	scan, err := txn.Iter(encodeKey(s.prefix, ""), nil)
	c.Assert(err, IsNil)
	cnt := 0
	for scan.Valid() {
		cnt++
		k := scan.Key()
		v := scan.Value()
		v2, ok := m[string(k)]
		c.Assert(ok, IsTrue, Commentf("key: %q", k))
		c.Assert(v, BytesEquals, v2)
		scan.Next()
	}
	err = txn.Commit(context.Background())
	c.Assert(err, IsNil)
	c.Assert(m, HasLen, cnt)
}

func (s *testSnapshotSuite) deleteKeys(keys []kv.Key, c *C) {
	txn := s.beginTxn(c)
	for _, k := range keys {
		err := txn.Delete(k)
		c.Assert(err, IsNil)
	}
	err := txn.Commit(context.Background())
	c.Assert(err, IsNil)
}

func (s *testSnapshotSuite) TestBatchGet(c *C) {
	for _, rowNum := range s.rowNums {
		logutil.BgLogger().Debug("test BatchGet",
			zap.Int("length", rowNum))
		txn := s.beginTxn(c)
		for i := 0; i < rowNum; i++ {
			k := encodeKey(s.prefix, s08d("key", i))
			err := txn.Set(k, valueBytes(i))
			c.Assert(err, IsNil)
		}
		err := txn.Commit(context.Background())
		c.Assert(err, IsNil)

		keys := makeKeys(rowNum, s.prefix)
		s.checkAll(keys, c)
		s.deleteKeys(keys, c)
	}
}

type contextKey string

func (s *testSnapshotSuite) TestSnapshotCache(c *C) {
	txn := s.beginTxn(c)
	c.Assert(txn.Set(kv.Key("x"), []byte("x")), IsNil)
	c.Assert(txn.Delete(kv.Key("y")), IsNil) // store data is affected by other tests.
	c.Assert(txn.Commit(context.Background()), IsNil)

	txn = s.beginTxn(c)
	snapshot := newTiKVSnapshot(s.store, kv.Version{Ver: txn.StartTS()}, 0)
	_, err := snapshot.BatchGet(context.Background(), []kv.Key{kv.Key("x"), kv.Key("y")})
	c.Assert(err, IsNil)

	c.Assert(failpoint.Enable("github.com/pingcap/tidb/store/tikv/snapshot-get-cache-fail", `return(true)`), IsNil)
	ctx := context.WithValue(context.Background(), contextKey("TestSnapshotCache"), true)
	_, err = snapshot.Get(ctx, kv.Key("x"))
	c.Assert(err, IsNil)

	_, err = snapshot.Get(ctx, kv.Key("y"))
	c.Assert(kv.IsErrNotFound(err), IsTrue)

	c.Assert(failpoint.Disable("github.com/pingcap/tidb/store/tikv/snapshot-get-cache-fail"), IsNil)
}

func (s *testSnapshotSuite) TestBatchGetNotExist(c *C) {
	for _, rowNum := range s.rowNums {
		logutil.BgLogger().Debug("test BatchGetNotExist",
			zap.Int("length", rowNum))
		txn := s.beginTxn(c)
		for i := 0; i < rowNum; i++ {
			k := encodeKey(s.prefix, s08d("key", i))
			err := txn.Set(k, valueBytes(i))
			c.Assert(err, IsNil)
		}
		err := txn.Commit(context.Background())
		c.Assert(err, IsNil)

		keys := makeKeys(rowNum, s.prefix)
		keys = append(keys, kv.Key("noSuchKey"))
		s.checkAll(keys, c)
		s.deleteKeys(keys, c)
	}
}

func makeKeys(rowNum int, prefix string) []kv.Key {
	keys := make([]kv.Key, 0, rowNum)
	for i := 0; i < rowNum; i++ {
		k := encodeKey(prefix, s08d("key", i))
		keys = append(keys, k)
	}
	return keys
}

func (s *testSnapshotSuite) TestWriteConflictPrettyFormat(c *C) {
	conflict := &pb.WriteConflict{
		StartTs:          399402937522847774,
		ConflictTs:       399402937719455772,
		ConflictCommitTs: 399402937719455773,
		Key:              []byte{116, 128, 0, 0, 0, 0, 0, 1, 155, 95, 105, 128, 0, 0, 0, 0, 0, 0, 1, 1, 82, 87, 48, 49, 0, 0, 0, 0, 251, 1, 55, 54, 56, 50, 50, 49, 49, 48, 255, 57, 0, 0, 0, 0, 0, 0, 0, 248, 1, 0, 0, 0, 0, 0, 0, 0, 0, 247},
		Primary:          []byte{116, 128, 0, 0, 0, 0, 0, 1, 155, 95, 105, 128, 0, 0, 0, 0, 0, 0, 1, 1, 82, 87, 48, 49, 0, 0, 0, 0, 251, 1, 55, 54, 56, 50, 50, 49, 49, 48, 255, 57, 0, 0, 0, 0, 0, 0, 0, 248, 1, 0, 0, 0, 0, 0, 0, 0, 0, 247},
	}

	expectedStr := "[kv:9007]Write conflict, " +
		"txnStartTS=399402937522847774, conflictStartTS=399402937719455772, conflictCommitTS=399402937719455773, " +
		"key={tableID=411, indexID=1, indexValues={RW01, 768221109, , }} " +
		"primary={tableID=411, indexID=1, indexValues={RW01, 768221109, , }} " +
		kv.TxnRetryableMark
	c.Assert(newWriteConflictError(conflict).Error(), Equals, expectedStr)

	conflict = &pb.WriteConflict{
		StartTs:          399402937522847774,
		ConflictTs:       399402937719455772,
		ConflictCommitTs: 399402937719455773,
		Key:              []byte{0x6d, 0x44, 0x42, 0x3a, 0x35, 0x36, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x54, 0x49, 0x44, 0x3a, 0x31, 0x30, 0x38, 0x0, 0xfe},
		Primary:          []byte{0x6d, 0x44, 0x42, 0x3a, 0x35, 0x36, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x54, 0x49, 0x44, 0x3a, 0x31, 0x30, 0x38, 0x0, 0xfe},
	}
	expectedStr = "[kv:9007]Write conflict, " +
		"txnStartTS=399402937522847774, conflictStartTS=399402937719455772, conflictCommitTS=399402937719455773, " +
		"key={metaKey=true, key=DB:56, field=TID:108} " +
		"primary={metaKey=true, key=DB:56, field=TID:108} " +
		kv.TxnRetryableMark
	c.Assert(newWriteConflictError(conflict).Error(), Equals, expectedStr)
}

func (s *testSnapshotSuite) TestLockNotFoundPrint(c *C) {
	msg := "Txn(Mvcc(TxnLockNotFound { start_ts: 408090278408224772, commit_ts: 408090279311835140, " +
		"key: [116, 128, 0, 0, 0, 0, 0, 50, 137, 95, 105, 128, 0, 0, 0, 0,0 ,0, 1, 1, 67, 49, 57, 48, 57, 50, 57, 48, 255, 48, 48, 48, 48, 48, 52, 56, 54, 255, 50, 53, 53, 50, 51, 0, 0, 0, 252] }))"
	key := prettyLockNotFoundKey(msg)
	c.Assert(key, Equals, "{tableID=12937, indexID=1, indexValues={C19092900000048625523, }}")
}

func (s *testSnapshotSuite) TestSkipLargeTxnLock(c *C) {
	x := kv.Key("x_key_TestSkipLargeTxnLock")
	y := kv.Key("y_key_TestSkipLargeTxnLock")
	txn := s.beginTxn(c)
	c.Assert(txn.Set(x, []byte("x")), IsNil)
	c.Assert(txn.Set(y, []byte("y")), IsNil)
	ctx := context.Background()
	bo := NewBackoffer(ctx, PrewriteMaxBackoff)
	committer, err := newTwoPhaseCommitterWithInit(txn, 0)
	c.Assert(err, IsNil)
	committer.lockTTL = 3000
	c.Assert(committer.prewriteKeys(bo, committer.keys), IsNil)

	txn1 := s.beginTxn(c)
	// txn1 is not blocked by txn in the large txn protocol.
	_, err = txn1.Get(ctx, kv.Key(x))
	c.Assert(kv.IsErrNotFound(errors.Trace(err)), IsTrue)

	res, err := txn1.BatchGet(ctx, []kv.Key{x, y, kv.Key("z")})
	c.Assert(err, IsNil)
	c.Assert(res, HasLen, 0)

	// Commit txn, check the final commit ts is pushed.
	committer.commitTS = txn.StartTS() + 1
	c.Assert(committer.commitKeys(bo, committer.keys), IsNil)
	status, err := s.store.lockResolver.GetTxnStatus(txn.StartTS(), 0, x)
	c.Assert(err, IsNil)
	c.Assert(status.IsCommitted(), IsTrue)
	c.Assert(status.CommitTS(), Greater, txn1.StartTS())
}
<|endoftext|>