<commit_before>package main

import "math/big"
import "testing"

// Benchmark isAKSWitness for the first prime number of the given
// number of decimal digits.
func runIsAKSWitnessBenchmark(b *testing.B, numDigits int64) {
	b.StopTimer()
	one := big.NewInt(1)
	n := big.NewInt(10)
	n.Exp(n, big.NewInt(numDigits), nil)
	rounds := 10
	for !n.ProbablyPrime(rounds) {
		n.Add(n, one)
	}
	r := calculateAKSModulus(n)
	// Any a > 1 suffices.
	a := big.NewInt(2)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		isAKSWitness(n, r, a)
	}
}

// Benchmark isAKSWitness for values of n of varying digit sizes.

func BenchmarkIsAKSWitness3Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 3)
}

func BenchmarkIsAKSWitness4Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 4)
}

func BenchmarkIsAKSWitness5Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 5)
}

func BenchmarkIsAKSWitness6Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 6)
}

func BenchmarkIsAKSWitness7Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 7)
}

func BenchmarkIsAKSWitness8Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 8)
}
<commit_msg>Add benchmarks for getFirstAKSWitness<commit_after>package main

import "io/ioutil"
import "log"
import "math/big"
import "testing"

// The number of rounds to use for big.Int.ProbablyPrime().
const _NUM_PROBABLY_PRIME_ROUNDS = 10

// Returns the first prime with the given number of decimal digits.
func getFirstPrimeWithDigits(numDigits int64) *big.Int {
	one := big.NewInt(1)
	n := big.NewInt(10)
	n.Exp(n, big.NewInt(numDigits), nil)
	for !n.ProbablyPrime(_NUM_PROBABLY_PRIME_ROUNDS) {
		n.Add(n, one)
	}
	return n
}

// Benchmark isAKSWitness for the first prime number of the given
// number of decimal digits.
func runIsAKSWitnessBenchmark(b *testing.B, numDigits int64) {
	b.StopTimer()
	n := getFirstPrimeWithDigits(numDigits)
	r := calculateAKSModulus(n)
	// Any a > 1 suffices.
	a := big.NewInt(2)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		isAKSWitness(n, r, a)
	}
}

// Benchmark isAKSWitness for values of n of varying digit sizes.

func BenchmarkIsAKSWitness3Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 3)
}

func BenchmarkIsAKSWitness4Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 4)
}

func BenchmarkIsAKSWitness5Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 5)
}

func BenchmarkIsAKSWitness6Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 6)
}

func BenchmarkIsAKSWitness7Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 7)
}

func BenchmarkIsAKSWitness8Digits(b *testing.B) {
	runIsAKSWitnessBenchmark(b, 8)
}

var nullLogger *log.Logger = log.New(ioutil.Discard, "", 0)

// Benchmark getFirstAKSWitness for the first prime number of the
// given number of decimal digits.
func runGetFirstAKSWitnessBenchmark(b *testing.B, numDigits int64) {
	b.StopTimer()
	n := getFirstPrimeWithDigits(numDigits)
	r := calculateAKSModulus(n)
	M := big.NewInt(10)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		getFirstAKSWitness(n, r, M, nullLogger)
	}
}

// Benchmark getFirstAKSWitness for values of n of varying digit sizes.

func BenchmarkGetFirstAKSWitness3Digits(b *testing.B) {
	runGetFirstAKSWitnessBenchmark(b, 3)
}

func BenchmarkGetFirstAKSWitness4Digits(b *testing.B) {
	runGetFirstAKSWitnessBenchmark(b, 4)
}

func BenchmarkGetFirstAKSWitness5Digits(b *testing.B) {
	runGetFirstAKSWitnessBenchmark(b, 5)
}

func BenchmarkGetFirstAKSWitness6Digits(b *testing.B) {
	runGetFirstAKSWitnessBenchmark(b, 6)
}

func BenchmarkGetFirstAKSWitness7Digits(b *testing.B) {
	runGetFirstAKSWitnessBenchmark(b, 7)
}

func BenchmarkGetFirstAKSWitness8Digits(b *testing.B) {
	runGetFirstAKSWitnessBenchmark(b, 8)
}
<|endoftext|>


<commit_before>package gosseract

import (
	"image"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"testing"

	"golang.org/x/net/html"

	. "github.com/otiai10/mint"
)

func TestMain(m *testing.M) {
	compromiseWhitelistAndBlacklistIfNotSupported()
	code := m.Run()
	os.Exit(code)
}

// @See https://github.com/otiai10/gosseract/issues/145
func compromiseWhitelistAndBlacklistIfNotSupported() {
	v := Version()
	v4, err := regexp.MatchString("4.0", v)
	if err != nil {
		log.Fatalln(err)
	}
	if v4 {
		os.Setenv("TESS_LSTM_DISABLED", "1")
	}
}

func TestVersion(t *testing.T) {
	version := Version()
	Expect(t, version).Match("[0-9]{1}.[0-9]{1,2}(.[0-9a-z_-]*)?")
}

func TestClearPersistentCache(t *testing.T) {
	client := NewClient()
	defer client.Close()
	client.init()
	ClearPersistentCache()
}

func TestNewClient(t *testing.T) {
	client := NewClient()
	defer client.Close()

	Expect(t, client).TypeOf("*gosseract.Client")
}

func TestClient_SetImage(t *testing.T) {
	client := NewClient()
	defer client.Close()

	client.Trim = true
	client.SetImage("./test/data/001-helloworld.png")

	client.SetPageSegMode(PSM_SINGLE_BLOCK)

	text, err := client.Text()
	if client.pixImage == nil {
		t.Errorf("could not set image")
	}
	Expect(t, err).ToBe(nil)
	Expect(t, text).ToBe("Hello, World!")

	err = client.SetImage("./test/data/001-helloworld.png")
	Expect(t, err).ToBe(nil)

	err = client.SetImage("")
	Expect(t, err).Not().ToBe(nil)

	err = client.SetImage("somewhere/fake/fakeimage.png")
	Expect(t, err).Not().ToBe(nil)

	_, err = client.Text()
	Expect(t, err).ToBe(nil)

	Because(t, "api must be initialized beforehand", func(t *testing.T) {
		client := &Client{}
		err := client.SetImage("./test/data/001-helloworld.png")
		Expect(t, err).Not().ToBe(nil)
	})
}

func TestClient_SetImageFromBytes(t *testing.T) {
	client := NewClient()
	defer client.Close()

	content, err := ioutil.ReadFile("./test/data/001-helloworld.png")
	if err != nil {
		t.Fatalf("could not read test file")
	}

	client.Trim = true
	client.SetImageFromBytes(content)

	client.SetPageSegMode(PSM_SINGLE_BLOCK)

	text, err := client.Text()
	if client.pixImage == nil {
		t.Errorf("could not set image")
	}
	Expect(t, err).ToBe(nil)
	Expect(t, text).ToBe("Hello, World!")
	err = client.SetImageFromBytes(content)
	Expect(t, err).ToBe(nil)

	err = client.SetImageFromBytes(nil)
	Expect(t, err).Not().ToBe(nil)

	Because(t, "api must be initialized beforehand", func(t *testing.T) {
		client := &Client{}
		err := client.SetImageFromBytes(content)
		Expect(t, err).Not().ToBe(nil)
	})
}

func TestClient_SetWhitelist(t *testing.T) {

	if os.Getenv("TESS_LSTM_DISABLED") == "1" {
		t.Skip("Whitelist with LSTM is not working for now. Please check https://github.com/tesseract-ocr/tesseract/issues/751")
	}

	client := NewClient()
	defer client.Close()

	client.Trim = true
	client.SetImage("./test/data/001-helloworld.png")
	client.Languages = []string{"eng"}
	client.SetWhitelist("HeloWrd,")
	text, err := client.Text()
	Expect(t, err).ToBe(nil)

	// Expect(t, text).ToBe("Hello, Worldl")
	Expect(t, text).Match("Hello, Worldl?")
}

func TestClient_SetBlacklist(t *testing.T) {

	if os.Getenv("TESS_LSTM_DISABLED") == "1" {
		t.Skip("Blacklist with LSTM is not working for now. Please check https://github.com/tesseract-ocr/tesseract/issues/751")
	}

	client := NewClient()
	defer client.Close()

	client.Trim = true
	err := client.SetImage("./test/data/001-helloworld.png")
	Expect(t, err).ToBe(nil)
	client.Languages = []string{"eng"}
	err = client.SetBlacklist("l")
	Expect(t, err).ToBe(nil)
	text, err := client.Text()
	Expect(t, err).ToBe(nil)
	Expect(t, text).ToBe("He110, WorId!")
}

func TestClient_SetLanguage(t *testing.T) {
	client := NewClient()
	defer client.Close()
	err := client.SetLanguage("deu")
	Expect(t, err).ToBe(nil)
	err = client.SetLanguage()
	Expect(t, err).Not().ToBe(nil)
	client.SetImage("./test/data/001-helloworld.png")
	_, err = client.Text()
	Expect(t, err).Not().ToBe(nil)
	if os.Getenv("GOSSERACT_CPPSTDERR_NOT_CAPTURED") != "1" {
		Expect(t, err).Match("Failed loading language 'deu'")
	}
}

func TestClient_ConfigFilePath(t *testing.T) {

	if os.Getenv("TESS_LSTM_DISABLED") == "1" {
		t.Skip("Whitelist with LSTM is not working for now. Please check https://github.com/tesseract-ocr/tesseract/issues/751")
	}

	client := NewClient()
	defer client.Close()

	err := client.SetConfigFile("./test/config/01.config")
	Expect(t, err).ToBe(nil)
	client.SetImage("./test/data/001-helloworld.png")
	text, err := client.Text()
	Expect(t, err).ToBe(nil)

	Expect(t, text).ToBe("H    W   ")

	When(t, "the config file is not found", func(t *testing.T) {
		err := client.SetConfigFile("./test/config/not-existing")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "the config file path is a directory", func(t *testing.T) {
		err := client.SetConfigFile("./test/config/02.config")
		Expect(t, err).Not().ToBe(nil)
	})

}

func TestClientBoundingBox(t *testing.T) {
	client := NewClient()
	defer client.Close()
	client.SetImage("./test/data/001-helloworld.png")
	client.SetWhitelist("Hello,World!")
	boxes, err := client.GetBoundingBoxes(RIL_WORD)
	Expect(t, err).ToBe(nil)

	Because(t, "api must be initialized beforehand", func(t *testing.T) {
		client := &Client{}
		_, err := client.GetBoundingBoxes(RIL_WORD)
		Expect(t, err).Not().ToBe(nil)
	})

	words := []string{"Hello,", "World!"}
	coords := []image.Rectangle{
		image.Rect(74, 64, 524, 190),
		image.Rect(638, 64, 1099, 170),
	}

	for i, box := range boxes {
		Expect(t, box.Word).ToBe(words[i])
		Expect(t, box.Box).ToBe(coords[i])
	}
}

func TestClient_HTML(t *testing.T) {
	client := NewClient()
	defer client.Close()
	client.SetImage("./test/data/001-helloworld.png")
	client.SetWhitelist("Hello,World!")
	out, err := client.HOCRText()
	Expect(t, err).ToBe(nil)

	tokenizer := html.NewTokenizer(strings.NewReader(out))

	texts := []string{}
	for ttype := tokenizer.Next(); ttype != html.ErrorToken; ttype = tokenizer.Next() {
		token := tokenizer.Token()
		if token.Type == html.TextToken && strings.TrimSpace(token.Data) != "" {
			texts = append(texts, strings.Trim(token.Data, "\n"))
		}
	}
	Expect(t, texts).ToBe([]string{"Hello,", "World!"})

	When(t, "only invalid languages are given", func(t *testing.T) {
		client := NewClient()
		defer client.Close()
		client.SetLanguage("foo")
		client.SetImage("./test/data/001-helloworld.png")
		_, err := client.HOCRText()
		Expect(t, err).Not().ToBe(nil)
	})
	Because(t, "unknown key is validated when `init` is called", func(t *testing.T) {
		client := NewClient()
		defer client.Close()
		err := client.SetVariable("foobar", "hoge")
		Expect(t, err).ToBe(nil)
		client.SetImage("./test/data/001-helloworld.png")
		_, err = client.Text()
		Expect(t, err).Not().ToBe(nil)
	})
}
<commit_msg>Do NOT use existing language for negative test case<commit_after>package gosseract

import (
	"image"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"testing"

	"golang.org/x/net/html"

	. "github.com/otiai10/mint"
)

func TestMain(m *testing.M) {
	compromiseWhitelistAndBlacklistIfNotSupported()
	code := m.Run()
	os.Exit(code)
}

// @See https://github.com/otiai10/gosseract/issues/145
func compromiseWhitelistAndBlacklistIfNotSupported() {
	v := Version()
	v4, err := regexp.MatchString("4.0", v)
	if err != nil {
		log.Fatalln(err)
	}
	if v4 {
		os.Setenv("TESS_LSTM_DISABLED", "1")
	}
}

func TestVersion(t *testing.T) {
	version := Version()
	Expect(t, version).Match("[0-9]{1}.[0-9]{1,2}(.[0-9a-z_-]*)?")
}

func TestClearPersistentCache(t *testing.T) {
	client := NewClient()
	defer client.Close()
	client.init()
	ClearPersistentCache()
}

func TestNewClient(t *testing.T) {
	client := NewClient()
	defer client.Close()

	Expect(t, client).TypeOf("*gosseract.Client")
}

func TestClient_SetImage(t *testing.T) {
	client := NewClient()
	defer client.Close()

	client.Trim = true
	client.SetImage("./test/data/001-helloworld.png")

	client.SetPageSegMode(PSM_SINGLE_BLOCK)

	text, err := client.Text()
	if client.pixImage == nil {
		t.Errorf("could not set image")
	}
	Expect(t, err).ToBe(nil)
	Expect(t, text).ToBe("Hello, World!")

	err = client.SetImage("./test/data/001-helloworld.png")
	Expect(t, err).ToBe(nil)

	err = client.SetImage("")
	Expect(t, err).Not().ToBe(nil)

	err = client.SetImage("somewhere/fake/fakeimage.png")
	Expect(t, err).Not().ToBe(nil)

	_, err = client.Text()
	Expect(t, err).ToBe(nil)

	Because(t, "api must be initialized beforehand", func(t *testing.T) {
		client := &Client{}
		err := client.SetImage("./test/data/001-helloworld.png")
		Expect(t, err).Not().ToBe(nil)
	})
}

func TestClient_SetImageFromBytes(t *testing.T) {
	client := NewClient()
	defer client.Close()

	content, err := ioutil.ReadFile("./test/data/001-helloworld.png")
	if err != nil {
		t.Fatalf("could not read test file")
	}

	client.Trim = true
	client.SetImageFromBytes(content)

	client.SetPageSegMode(PSM_SINGLE_BLOCK)

	text, err := client.Text()
	if client.pixImage == nil {
		t.Errorf("could not set image")
	}
	Expect(t, err).ToBe(nil)
	Expect(t, text).ToBe("Hello, World!")
	err = client.SetImageFromBytes(content)
	Expect(t, err).ToBe(nil)

	err = client.SetImageFromBytes(nil)
	Expect(t, err).Not().ToBe(nil)

	Because(t, "api must be initialized beforehand", func(t *testing.T) {
		client := &Client{}
		err := client.SetImageFromBytes(content)
		Expect(t, err).Not().ToBe(nil)
	})
}

func TestClient_SetWhitelist(t *testing.T) {

	if os.Getenv("TESS_LSTM_DISABLED") == "1" {
		t.Skip("Whitelist with LSTM is not working for now. Please check https://github.com/tesseract-ocr/tesseract/issues/751")
	}

	client := NewClient()
	defer client.Close()

	client.Trim = true
	client.SetImage("./test/data/001-helloworld.png")
	client.Languages = []string{"eng"}
	client.SetWhitelist("HeloWrd,")
	text, err := client.Text()
	Expect(t, err).ToBe(nil)

	// Expect(t, text).ToBe("Hello, Worldl")
	Expect(t, text).Match("Hello, Worldl?")
}

func TestClient_SetBlacklist(t *testing.T) {

	if os.Getenv("TESS_LSTM_DISABLED") == "1" {
		t.Skip("Blacklist with LSTM is not working for now. Please check https://github.com/tesseract-ocr/tesseract/issues/751")
	}

	client := NewClient()
	defer client.Close()

	client.Trim = true
	err := client.SetImage("./test/data/001-helloworld.png")
	Expect(t, err).ToBe(nil)
	client.Languages = []string{"eng"}
	err = client.SetBlacklist("l")
	Expect(t, err).ToBe(nil)
	text, err := client.Text()
	Expect(t, err).ToBe(nil)
	Expect(t, text).ToBe("He110, WorId!")
}

func TestClient_SetLanguage(t *testing.T) {
	client := NewClient()
	defer client.Close()
	err := client.SetLanguage("undefined-language")
	Expect(t, err).ToBe(nil)
	err = client.SetLanguage()
	Expect(t, err).Not().ToBe(nil)
	client.SetImage("./test/data/001-helloworld.png")
	_, err = client.Text()
	Expect(t, err).Not().ToBe(nil)
	if os.Getenv("GOSSERACT_CPPSTDERR_NOT_CAPTURED") != "1" {
		Expect(t, err).Match("Failed loading language 'undefined-language'")
	}
}

func TestClient_ConfigFilePath(t *testing.T) {

	if os.Getenv("TESS_LSTM_DISABLED") == "1" {
		t.Skip("Whitelist with LSTM is not working for now. Please check https://github.com/tesseract-ocr/tesseract/issues/751")
	}

	client := NewClient()
	defer client.Close()

	err := client.SetConfigFile("./test/config/01.config")
	Expect(t, err).ToBe(nil)
	client.SetImage("./test/data/001-helloworld.png")
	text, err := client.Text()
	Expect(t, err).ToBe(nil)

	Expect(t, text).ToBe("H    W   ")

	When(t, "the config file is not found", func(t *testing.T) {
		err := client.SetConfigFile("./test/config/not-existing")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "the config file path is a directory", func(t *testing.T) {
		err := client.SetConfigFile("./test/config/02.config")
		Expect(t, err).Not().ToBe(nil)
	})

}

func TestClientBoundingBox(t *testing.T) {
	client := NewClient()
	defer client.Close()
	client.SetImage("./test/data/001-helloworld.png")
	client.SetWhitelist("Hello,World!")
	boxes, err := client.GetBoundingBoxes(RIL_WORD)
	Expect(t, err).ToBe(nil)

	Because(t, "api must be initialized beforehand", func(t *testing.T) {
		client := &Client{}
		_, err := client.GetBoundingBoxes(RIL_WORD)
		Expect(t, err).Not().ToBe(nil)
	})

	words := []string{"Hello,", "World!"}
	coords := []image.Rectangle{
		image.Rect(74, 64, 524, 190),
		image.Rect(638, 64, 1099, 170),
	}

	for i, box := range boxes {
		Expect(t, box.Word).ToBe(words[i])
		Expect(t, box.Box).ToBe(coords[i])
	}
}

func TestClient_HTML(t *testing.T) {
	client := NewClient()
	defer client.Close()
	client.SetImage("./test/data/001-helloworld.png")
	client.SetWhitelist("Hello,World!")
	out, err := client.HOCRText()
	Expect(t, err).ToBe(nil)

	tokenizer := html.NewTokenizer(strings.NewReader(out))

	texts := []string{}
	for ttype := tokenizer.Next(); ttype != html.ErrorToken; ttype = tokenizer.Next() {
		token := tokenizer.Token()
		if token.Type == html.TextToken && strings.TrimSpace(token.Data) != "" {
			texts = append(texts, strings.Trim(token.Data, "\n"))
		}
	}
	Expect(t, texts).ToBe([]string{"Hello,", "World!"})

	When(t, "only invalid languages are given", func(t *testing.T) {
		client := NewClient()
		defer client.Close()
		client.SetLanguage("foo")
		client.SetImage("./test/data/001-helloworld.png")
		_, err := client.HOCRText()
		Expect(t, err).Not().ToBe(nil)
	})
	Because(t, "unknown key is validated when `init` is called", func(t *testing.T) {
		client := NewClient()
		defer client.Close()
		err := client.SetVariable("foobar", "hoge")
		Expect(t, err).ToBe(nil)
		client.SetImage("./test/data/001-helloworld.png")
		_, err = client.Text()
		Expect(t, err).Not().ToBe(nil)
	})
}
<|endoftext|>


<commit_before>package annotate

import (
	"bytes"
	"errors"
	"io"
	"unicode/utf8"
)

type Annotation struct {
	Start, End  int
	Left, Right []byte
	WantInner   int
}

type Annotations []*Annotation

func (a Annotations) Len() int { return len(a) }
func (a Annotations) Less(i, j int) bool {
	// Sort by start position, breaking ties by preferring longer
	// matches.
	ai, aj := a[i], a[j]
	if ai.Start == aj.Start {
		if ai.End == aj.End {
			return ai.WantInner < aj.WantInner
		}
		return ai.End > aj.End
	} else {
		return ai.Start < aj.Start
	}
}
func (a Annotations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

// Annotates src with annotations in anns.
//
// Annotating an empty byte array always returns an empty byte array.
//
// Assumes anns is sorted (using sort.Sort(anns)).
func Annotate(src []byte, anns Annotations, writeContent func(io.Writer, rune)) ([]byte, error) {
	var out bytes.Buffer
	var err error

	// Default content writer.
	if writeContent == nil {
		writeContent = func(w io.Writer, c rune) { io.WriteString(w, string(c)) }
	}

	// Keep a stack of annotations we should close at all future rune offsets.
	closeAnnsAtRune := make(map[int]Annotations, len(src)/10)
	closeAnnsAt := func(r int) {
		// log.Printf("closeAnnsAt(%d)", r)
		// Close annotations that after this rune.
		if closeAnns, present := closeAnnsAtRune[r]; present {
			for i := len(closeAnns) - 1; i >= 0; i-- {
				out.Write(closeAnns[i].Right)
			}
			delete(closeAnnsAtRune, r)
		}
	}

	runeCount := utf8.RuneCount(src)
	b := 0
	for r := 0; r < runeCount; r++ {
		// Open annotations that begin here.
		for i, a := range anns {
			if a.Start == r {
				out.Write(a.Left)

				if a.Start == a.End {
					out.Write(a.Right)
				} else {
					// Put this annotation on the stack of annotations that will need
					// to be closed. We remove it from anns at the end of the loop
					// (to avoid modifying anns while we're iterating over it).
					closeAnnsAtRune[a.End] = append(closeAnnsAtRune[a.End], a)
				}
			} else if a.Start > r {
				// Remove all annotations that we opened (we already put them on the
				// stack of annotations that will need to be closed).
				anns = anns[i:]
				break
			} else if a.Start < 0 {
				err = ErrStartOutOfBounds
			}
		}

		rune, runeSize := utf8.DecodeRune(src)
		src = src[runeSize:]
		b += runeSize

		writeContent(&out, rune)

		closeAnnsAt(r + 1)
	}

	if len(closeAnnsAtRune) > 0 {
		err = ErrEndOutOfBounds
	}

	return out.Bytes(), err
}

var (
	ErrStartOutOfBounds = errors.New("annotation start out of bounds")
	ErrEndOutOfBounds   = errors.New("annotation end out of bounds")
)
<commit_msg>inline function<commit_after>package annotate

import (
	"bytes"
	"errors"
	"io"
	"unicode/utf8"
)

type Annotation struct {
	Start, End  int
	Left, Right []byte
	WantInner   int
}

type Annotations []*Annotation

func (a Annotations) Len() int { return len(a) }
func (a Annotations) Less(i, j int) bool {
	// Sort by start position, breaking ties by preferring longer
	// matches.
	ai, aj := a[i], a[j]
	if ai.Start == aj.Start {
		if ai.End == aj.End {
			return ai.WantInner < aj.WantInner
		}
		return ai.End > aj.End
	} else {
		return ai.Start < aj.Start
	}
}
func (a Annotations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

// Annotates src with annotations in anns.
//
// Annotating an empty byte array always returns an empty byte array.
//
// Assumes anns is sorted (using sort.Sort(anns)).
func Annotate(src []byte, anns Annotations, writeContent func(io.Writer, rune)) ([]byte, error) {
	var out bytes.Buffer
	var err error

	// Default content writer.
	if writeContent == nil {
		writeContent = func(w io.Writer, c rune) { io.WriteString(w, string(c)) }
	}

	// Keep a stack of annotations we should close at all future rune offsets.
	closeAnnsAtRune := make(map[int]Annotations, len(src)/10)

	runeCount := utf8.RuneCount(src)
	b := 0
	for r := 0; r < runeCount; r++ {
		// Open annotations that begin here.
		for i, a := range anns {
			if a.Start == r {
				out.Write(a.Left)

				if a.Start == a.End {
					out.Write(a.Right)
				} else {
					// Put this annotation on the stack of annotations that will need
					// to be closed. We remove it from anns at the end of the loop
					// (to avoid modifying anns while we're iterating over it).
					closeAnnsAtRune[a.End] = append(closeAnnsAtRune[a.End], a)
				}
			} else if a.Start > r {
				// Remove all annotations that we opened (we already put them on the
				// stack of annotations that will need to be closed).
				anns = anns[i:]
				break
			} else if a.Start < 0 {
				err = ErrStartOutOfBounds
			}
		}

		rune, runeSize := utf8.DecodeRune(src)
		src = src[runeSize:]
		b += runeSize

		writeContent(&out, rune)

		// Close annotations that after this rune.
		if closeAnns, present := closeAnnsAtRune[r+1]; present {
			for i := len(closeAnns) - 1; i >= 0; i-- {
				out.Write(closeAnns[i].Right)
			}
			delete(closeAnnsAtRune, r+1)
		}
	}

	if len(closeAnnsAtRune) > 0 {
		err = ErrEndOutOfBounds
	}

	return out.Bytes(), err
}

var (
	ErrStartOutOfBounds = errors.New("annotation start out of bounds")
	ErrEndOutOfBounds   = errors.New("annotation end out of bounds")
)
<|endoftext|>


<commit_before>package middleware

import (
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/gilcrest/go-API-template/pkg/env"
	"github.com/rs/zerolog/log"
)

// LogResponse records and logs the response code, header and body details
// using an httptest.ResponseRecorder.  This function also manages the
// request/response timing
func LogResponse(env *env.Env, aud *APIAudit) Adapter {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

			var (
				err error
			)

			log.Print("Start LogResponse")
			defer log.Print("Finish LogResponse")

			startTimer(aud)

			rec := httptest.NewRecorder()
			h.ServeHTTP(rec, r)

			// copy everything from response recorder
			// to actual response writer
			for k, v := range rec.HeaderMap {
				w.Header()[k] = v
			}
			w.WriteHeader(rec.Code)

			// pull out the response body and write it
			// back to the response writer
			b := rec.Body.Bytes()
			w.Write(b)

			stopTimer(aud)

			// write the data back to the recorder buffer as
			// it's needed for SetResponse
			rec.Body.Write(b)

			// set the response data in the APIAudit object
			err = setResponse(aud, rec)
			if err != nil {
				log.Error().Err(err).Msg("")
				http.Error(w, "Unable to set response", http.StatusBadRequest)
			}

			// call logRespDispatch to determine if and where to log
			err = logDispatch(env, aud)
			if err != nil {
				log.Error().Err(err).Msg("")
			}

		})
	}
}

// sets the start time in the APIAudit object
func startTimer(aud *APIAudit) {
	// set APIAudit TimeStarted to current time in UTC
	loc, _ := time.LoadLocation("UTC")
	aud.TimeStarted = time.Now().In(loc)
}

// stopTimer sets the stop time in the APIAudit object and
// subtracts the stop time from the start time to determine the
// service execution duration as this is after the response
// has been written and sent
func stopTimer(aud *APIAudit) {
	loc, _ := time.LoadLocation("UTC")
	aud.TimeFinished = time.Now().In(loc)
	duration := aud.TimeFinished.Sub(aud.TimeStarted)
	aud.Duration = duration
}

// SetResponse sets the response elements of the APIAudit payload
func setResponse(aud *APIAudit, rec *httptest.ResponseRecorder) error {
	// set ResponseCode from ResponseRecorder
	aud.ResponseCode = rec.Code

	// set Header JSON from Header map in ResponseRecorder
	headerJSON, err := convertHeader(rec.HeaderMap)
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	aud.response.Header = headerJSON

	// Dump body to text using dumpBody function - need an http request
	// struct, so use httptest.NewRequest to get one
	req := httptest.NewRequest("POST", "http://example.com/foo", rec.Body)

	body, err := dumpBody(req)
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	aud.response.Body = body

	return nil
}

// logRespDispatch determines which, if any, of the logging methods
// you wish to use will be employed
func logDispatch(env *env.Env, aud *APIAudit) error {
	if env.LogOpts.Log2StdOut.Response.Enable {
		logResp2Stdout(env, aud)
	}

	if env.LogOpts.Log2DB.Enable {
		err := logReqResp2Db(env, aud)
		if err != nil {
			log.Error().Err(err).Msg("")
			return err
		}
	}
	return nil
}

func logResp2Stdout(env *env.Env, aud *APIAudit) {
	logger := env.Logger

	logger.Debug().Msg("logResponse started")
	defer logger.Debug().Msg("logResponse ended")

	logger.Info().
		Str("request_id", aud.RequestID).
		Int("response_code", aud.ResponseCode).
		Str("response_header", aud.response.Header).
		Str("response_body", aud.response.Body).
		Msg("Response Sent")
}

// Creates a record in the appUser table using a stored function
func logReqResp2Db(env *env.Env, aud *APIAudit) error {

	var (
		rowsInserted int
		respHdr      interface{}
		respBody     interface{}
		reqHdr       interface{}
		reqBody      interface{}
	)

	if env.LogOpts.Log2DB.Enable {
		reqHdr = nil
		if env.LogOpts.Log2DB.Request.Header {
			// This empty string to nil conversion is probably
			// not necessary, but just in case to avoid db exception
			reqHdr = strNil(aud.request.Header)
		}
		reqBody = nil
		if env.LogOpts.Log2DB.Request.Body {
			// This empty string to nil conversion is probably
			// not necessary, but just in case to avoid db exception
			reqBody = strNil(aud.request.Body)
		}
	}

	if env.LogOpts.Log2DB.Enable {
		respHdr = nil
		if env.LogOpts.Log2DB.Response.Header {
			// This empty string to nil conversion is probably
			// not necessary, but just in case to avoid db exception
			respHdr = strNil(aud.response.Header)
		}
		respBody = nil
		if env.LogOpts.Log2DB.Response.Body {
			// This empty string to nil conversion is probably
			// not necessary, but just in case to avoid db exception
			respBody = strNil(aud.response.Body)
		}
	}

	// Calls the BeginTx method of the LogDB opened database
	tx, err := env.DS.LogDb.BeginTx(aud.ctx, nil)
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}

	// time.Duration is in nanoseconds,
	// need to do below math for milliseconds
	durMS := aud.Duration / time.Millisecond

	// Prepare the sql statement using bind variables
	stmt, err := tx.PrepareContext(aud.ctx, `select api.log_request
		(
		p_request_id => $1,
		p_request_timestamp => $2,
		p_response_code => $3,
		p_response_timestamp => $4,
		p_duration_in_millis => $5,
		p_protocol => $6,
		p_protocol_major => $7,
		p_protocol_minor => $8,
		p_request_method => $9,
		p_scheme => $10,
		p_host => $11,
		p_port => $12,
		p_path => $13,
		p_remote_address => $14,
		p_request_content_length => $15,
		p_request_header => $16,
		p_request_body => $17,
		p_response_header => $18,
		p_response_body => $19)`)

	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	defer stmt.Close()

	rows, err := stmt.QueryContext(aud.ctx,
		aud.RequestID,             //$1
		aud.TimeStarted,           //$2
		aud.ResponseCode,          //$3
		aud.TimeFinished,          //$4
		durMS,                     //$5
		aud.request.Proto,         //$6
		aud.request.ProtoMajor,    //$7
		aud.request.ProtoMinor,    //$8
		aud.request.Method,        //$9
		aud.request.Scheme,        //$10
		aud.request.Host,          //$11
		aud.request.Port,          //$12
		aud.request.Path,          //$13
		aud.request.RemoteAddr,    //$14
		aud.request.ContentLength, //$15
		reqHdr,   //$16
		reqBody,  //$17
		respHdr,  //$18
		respBody) //$19

	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	defer rows.Close()

	// Iterate through the returned record(s)
	for rows.Next() {
		if err := rows.Scan(&rowsInserted); err != nil {
			log.Error().Err(err).Msg("")
			return err
		}
	}

	err = rows.Err()
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}

	// If we have successfully written rows to the db
	// we commit the transaction
	err = tx.Commit()
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}

	return nil

}

// strNil checks if the header field is an empty string
// (the empty value for the string type) and switches it to
// a nil.  An empty string is not allowed to be passed to a
// JSONB type in postgres, however, a nil works
func strNil(s string) interface{} {
	var v interface{}

	v = s
	if s == "" {
		v = nil
	}

	return v
}
<commit_msg>Better doc and reorg<commit_after>package middleware

import (
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/gilcrest/go-API-template/pkg/env"
	"github.com/rs/zerolog/log"
)

// LogResponse records and logs the response code, header and body details
// using an httptest.ResponseRecorder.  This function also manages the
// request/response timing
func LogResponse(env *env.Env, aud *APIAudit) Adapter {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

			log.Print("Start LogResponse")
			defer log.Print("Finish LogResponse")

			startTimer(aud)

			rec := httptest.NewRecorder()
			h.ServeHTTP(rec, r)

			// copy everything from response recorder
			// to actual response writer
			for k, v := range rec.HeaderMap {
				w.Header()[k] = v
			}
			w.WriteHeader(rec.Code)

			// pull out the response body and write it
			// back to the response writer
			b := rec.Body.Bytes()
			w.Write(b)

			stopTimer(aud)

			// write the data back to the recorder buffer as
			// it's needed for SetResponse
			rec.Body.Write(b)

			// set the response data in the APIAudit object
			err := setResponse(aud, rec)
			if err != nil {
				log.Error().Err(err).Msg("")
				http.Error(w, "Unable to set response", http.StatusBadRequest)
			}

			// call logRespDispatch to determine if and where to log
			err = logRespDispatch(env, aud)
			if err != nil {
				log.Error().Err(err).Msg("")
			}

		})
	}
}

// sets the start time in the APIAudit object
func startTimer(aud *APIAudit) {
	// set APIAudit TimeStarted to current time in UTC
	loc, _ := time.LoadLocation("UTC")
	aud.TimeStarted = time.Now().In(loc)
}

// stopTimer sets the stop time in the APIAudit object and
// subtracts the stop time from the start time to determine the
// service execution duration as this is after the response
// has been written and sent
func stopTimer(aud *APIAudit) {
	loc, _ := time.LoadLocation("UTC")
	aud.TimeFinished = time.Now().In(loc)
	duration := aud.TimeFinished.Sub(aud.TimeStarted)
	aud.Duration = duration
}

// SetResponse sets the response elements of the APIAudit payload
func setResponse(aud *APIAudit, rec *httptest.ResponseRecorder) error {
	// set ResponseCode from ResponseRecorder
	aud.ResponseCode = rec.Code

	// set Header JSON from Header map in ResponseRecorder
	headerJSON, err := convertHeader(rec.HeaderMap)
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	aud.response.Header = headerJSON

	// Dump body to text using dumpBody function - need an http request
	// struct, so use httptest.NewRequest to get one
	req := httptest.NewRequest("POST", "http://example.com/foo", rec.Body)

	body, err := dumpBody(req)
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	aud.response.Body = body

	return nil
}

// logRespDispatch determines which, if any, of the logging methods
// you wish to use will be employed
func logRespDispatch(env *env.Env, aud *APIAudit) error {
	if env.LogOpts.Log2StdOut.Response.Enable {
		logResp2Stdout(env, aud)
	}

	if env.LogOpts.Log2DB.Enable {
		err := logReqResp2Db(env, aud)
		if err != nil {
			log.Error().Err(err).Msg("")
			return err
		}
	}
	return nil
}

func logResp2Stdout(env *env.Env, aud *APIAudit) {
	logger := env.Logger

	logger.Debug().Msg("logResponse started")
	defer logger.Debug().Msg("logResponse ended")

	logger.Info().
		Str("request_id", aud.RequestID).
		Int("response_code", aud.ResponseCode).
		Str("response_header", aud.response.Header).
		Str("response_body", aud.response.Body).
		Msg("Response Sent")
}

// logReqResp2Db creates a record in the api.audit_log table
// using a stored function
func logReqResp2Db(env *env.Env, aud *APIAudit) error {

	var (
		rowsInserted int
		respHdr      interface{}
		respBody     interface{}
		reqHdr       interface{}
		reqBody      interface{}
	)

	// default reqHdr variable to nil
	// if the Request Header logging option is enabled for db logging
	// then check if the header string is it's zero value and if so,
	// switch it to nil, otherwise write it to the variable
	reqHdr = nil
	if env.LogOpts.Log2DB.Request.Header {
		// This empty string to nil conversion is probably
		// not necessary, but just in case to avoid db exception
		reqHdr = strNil(aud.request.Header)
	}
	// default reqBody variable to nil
	// if the Request Body logging option is enabled for db logging
	// then check if the header string is it's zero value and if so,
	// switch it to nil, otherwise write it to the variable
	reqBody = nil
	if env.LogOpts.Log2DB.Request.Body {
		reqBody = strNil(aud.request.Body)
	}
	// default respHdr variable to nil
	// if the Response Header logging option is enabled for db logging
	// then check if the header string is it's zero value and if so,
	// switch it to nil, otherwise write it to the variable
	respHdr = nil
	if env.LogOpts.Log2DB.Response.Header {
		respHdr = strNil(aud.response.Header)
	}
	// default respBody variable to nil
	// if the Response Body logging option is enabled for db logging
	// then check if the header string is it's zero value and if so,
	// switch it to nil, otherwise write it to the variable
	respBody = nil
	if env.LogOpts.Log2DB.Response.Body {
		respBody = strNil(aud.response.Body)
	}

	// Calls the BeginTx method of the LogDB opened database
	tx, err := env.DS.LogDb.BeginTx(aud.ctx, nil)
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}

	// time.Duration is in nanoseconds,
	// need to do below math for milliseconds
	durMS := aud.Duration / time.Millisecond

	// Prepare the sql statement using bind variables
	stmt, err := tx.PrepareContext(aud.ctx, `select api.log_request
		(
		p_request_id => $1,
		p_request_timestamp => $2,
		p_response_code => $3,
		p_response_timestamp => $4,
		p_duration_in_millis => $5,
		p_protocol => $6,
		p_protocol_major => $7,
		p_protocol_minor => $8,
		p_request_method => $9,
		p_scheme => $10,
		p_host => $11,
		p_port => $12,
		p_path => $13,
		p_remote_address => $14,
		p_request_content_length => $15,
		p_request_header => $16,
		p_request_body => $17,
		p_response_header => $18,
		p_response_body => $19)`)

	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	defer stmt.Close()

	rows, err := stmt.QueryContext(aud.ctx,
		aud.RequestID,             //$1
		aud.TimeStarted,           //$2
		aud.ResponseCode,          //$3
		aud.TimeFinished,          //$4
		durMS,                     //$5
		aud.request.Proto,         //$6
		aud.request.ProtoMajor,    //$7
		aud.request.ProtoMinor,    //$8
		aud.request.Method,        //$9
		aud.request.Scheme,        //$10
		aud.request.Host,          //$11
		aud.request.Port,          //$12
		aud.request.Path,          //$13
		aud.request.RemoteAddr,    //$14
		aud.request.ContentLength, //$15
		reqHdr,   //$16
		reqBody,  //$17
		respHdr,  //$18
		respBody) //$19

	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}
	defer rows.Close()

	// Iterate through the returned record(s)
	for rows.Next() {
		if err := rows.Scan(&rowsInserted); err != nil {
			log.Error().Err(err).Msg("")
			return err
		}
	}

	err = rows.Err()
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}

	// If we have successfully written rows to the db
	// we commit the transaction
	err = tx.Commit()
	if err != nil {
		log.Error().Err(err).Msg("")
		return err
	}

	return nil

}

// strNil checks if the header field is an empty string
// (the empty value for the string type) and switches it to
// a nil.  An empty string is not allowed to be passed to a
// JSONB type in postgres, however, a nil works
func strNil(s string) interface{} {
	var v interface{}

	v = s
	if s == "" {
		v = nil
	}

	return v
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The OpenEBS 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 v1alpha1
import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// CasPool is a type which will be utilised by CAS engine to perform
// storagepool related operation
type CasPoolKey string
type CasPoolValString string
type CasPoolValInt int

const (
	// HostNameCPK is the kubernetes host name label
	HostNameCPK CasPoolKey = "kubernetes.io/hostname"
	// StoragePoolClaimCPK is the storage pool claim label
	StoragePoolClaimCPK CasPoolKey = "openebs.io/storage-pool-claim"
	// DiskTypeCPK is the node-disk-manager disk type e.g. 'sparse' or 'disk'
	NdmDiskTypeCPK CasPoolKey = "ndm.io/disk-type"
	// PoolTypeMirroredCPK is a key for mirrored for pool
	PoolTypeMirroredCPV CasPoolValString = "mirrored"
	// PoolTypeMirroredCPK is a key for striped for pool
	PoolTypeStripedCPV CasPoolValString = "striped"
	// TypeSparseCPK is a key for sparse disk pool
	TypeSparseCPV CasPoolValString = "sparse"
	// TypeDiskCPK is a key for physical,iscsi,virtual etc disk pool
	TypeDiskCPV CasPoolValString = "disk"
	// StripedDiskCountCPK is the count for striped type pool
	StripedDiskCountCPV CasPoolValInt = 1
	// MirroredDiskCountCPK is the count for mirrored type pool
	MirroredDiskCountCPV CasPoolValInt = 2
)

// TODO : Restructure the CasPool struct
type CasPool struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	// StoragePoolClaim is the name of the storagepoolclaim object
	StoragePoolClaim string

	// CasCreateTemplate is the cas template that will be used for storagepool create
	// operation
	CasCreateTemplate string

	// CasDeleteTemplate is the cas template that will be used for storagepool delete
	// operation
	CasDeleteTemplate string

	// Namespace can be passed via storagepoolclaim as labels to decide on the
	// execution of namespaced resources with respect to storagepool
	Namespace string

	// DiskList is the list of disks over which a storagepool will be provisioned
	DiskList []string

	// PoolType is the type of pool to be provisioned e.g. striped or mirrored
	PoolType string

	// MaxPool is the maximum number of pool that should be provisioned
	MaxPools int

	// MinPool is the minimum number of pool that should be provisioned
	MinPools int

	// Type is the CasPool type e.g. sparse or openebs-cstor
	Type string

	// reSync will decide whether the event is a reconciliation event
	ReSync bool

	// PendingPoolCount is the number of pools that will be tried for creation as a part of reconciliation.
	PendingPoolCount int
}
<commit_msg>Fix golint issues<commit_after>/*
Copyright 2017 The OpenEBS 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 v1alpha1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// CasPoolKey is the key for the CasPool.
type CasPoolKey string

// CasPoolValString represents the string value for a CasPoolKey.
type CasPoolValString string

// CasPoolValInt represents the integer value for a CasPoolKey
type CasPoolValInt int

const (
	// HostNameCPK is the kubernetes host name label
	HostNameCPK CasPoolKey = "kubernetes.io/hostname"
	// StoragePoolClaimCPK is the storage pool claim label
	StoragePoolClaimCPK CasPoolKey = "openebs.io/storage-pool-claim"
	// NdmDiskTypeCPK is the node-disk-manager disk type e.g. 'sparse' or 'disk'
	NdmDiskTypeCPK CasPoolKey = "ndm.io/disk-type"
	// PoolTypeMirroredCPV is a key for mirrored for pool
	PoolTypeMirroredCPV CasPoolValString = "mirrored"
	// PoolTypeStripedCPV is a key for striped for pool
	PoolTypeStripedCPV CasPoolValString = "striped"
	// TypeSparseCPV is a key for sparse disk pool
	TypeSparseCPV CasPoolValString = "sparse"
	// TypeDiskCPV is a key for physical,iscsi,virtual etc disk pool
	TypeDiskCPV CasPoolValString = "disk"
	// StripedDiskCountCPV is the count for striped type pool
	StripedDiskCountCPV CasPoolValInt = 1
	// MirroredDiskCountCPV is the count for mirrored type pool
	MirroredDiskCountCPV CasPoolValInt = 2
)

// CasPool is a type which will be utilised by CAS engine to perform
// storagepool related operation.
// TODO: Restrucutre CasPool struct.
type CasPool struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	// StoragePoolClaim is the name of the storagepoolclaim object
	StoragePoolClaim string

	// CasCreateTemplate is the cas template that will be used for storagepool create
	// operation
	CasCreateTemplate string

	// CasDeleteTemplate is the cas template that will be used for storagepool delete
	// operation
	CasDeleteTemplate string

	// Namespace can be passed via storagepoolclaim as labels to decide on the
	// execution of namespaced resources with respect to storagepool
	Namespace string

	// DiskList is the list of disks over which a storagepool will be provisioned
	DiskList []string

	// PoolType is the type of pool to be provisioned e.g. striped or mirrored
	PoolType string

	// MaxPool is the maximum number of pool that should be provisioned
	MaxPools int

	// MinPool is the minimum number of pool that should be provisioned
	MinPools int

	// Type is the CasPool type e.g. sparse or openebs-cstor
	Type string

	// reSync will decide whether the event is a reconciliation event
	ReSync bool

	// PendingPoolCount is the number of pools that will be tried for creation as a part of reconciliation.
	PendingPoolCount int
}
<|endoftext|>


<commit_before>package common

import (
	"encoding/base32"

	"github.com/pkg/errors"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/slice"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/rancher/types/user"
	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/tools/cache"
)

const (
	userAuthHeader       = "Impersonate-User"
	userByPrincipalIndex = "auth.management.cattle.io/userByPrincipal"
)

func NewUserManager(scaledContext *config.ScaledContext) (user.Manager, error) {
	userInformer := scaledContext.Management.Users("").Controller().Informer()
	userIndexers := map[string]cache.IndexFunc{
		userByPrincipalIndex: userByPrincipal,
	}
	if err := userInformer.AddIndexers(userIndexers); err != nil {
		return nil, err
	}

	return &userManager{
		users:              scaledContext.Management.Users(""),
		userIndexer:        userInformer.GetIndexer(),
		globalRoleBindings: scaledContext.Management.GlobalRoleBindings(""),
	}, nil
}

type userManager struct {
	users              v3.UserInterface
	globalRoleBindings v3.GlobalRoleBindingInterface
	userIndexer        cache.Indexer
}

func (m *userManager) SetPrincipalOnCurrentUser(apiContext *types.APIContext, principal v3.Principal) (*v3.User, error) {
	userID := m.GetUser(apiContext)
	if userID == "" {
		return nil, errors.New("user not provided")
	}

	user, err := m.users.Get(userID, v1.GetOptions{})
	if err != nil {
		return nil, err
	}

	if !slice.ContainsString(user.PrincipalIDs, principal.Name) {
		user.PrincipalIDs = append(user.PrincipalIDs, principal.Name)
		return m.users.Update(user)
	}
	return user, nil
}

func (m *userManager) GetUser(apiContext *types.APIContext) string {
	return apiContext.Request.Header.Get(userAuthHeader)
}

// checkis if the supplied principal can login based on the accessMode and allowed principals
func (m *userManager) CheckAccess(accessMode string, allowedPrincipalIDs []string, user v3.Principal, groups []v3.Principal) (bool, error) {
	if accessMode == "unrestricted" {
		return true, nil
	}

	if accessMode == "required" || accessMode == "restricted" {
		if slice.ContainsString(allowedPrincipalIDs, user.Name) {
			return true, nil
		}
		for _, g := range groups {
			if slice.ContainsString(allowedPrincipalIDs, g.Name) {
				return true, nil
			}
		}
		if accessMode == "restricted" {
			u, err := m.checkCache(user.Name)
			if err != nil {
				return false, err
			}
			if u != nil {
				return true, nil
			}

			// Not in cache, query API by label
			u, _, err = m.checkLabels(user.Name)
			if err != nil {
				return false, err
			}
			if u != nil {
				return true, nil
			}
		}
		return false, nil
	}
	return false, errors.Errorf("Unsupport accessMode: %v", accessMode)
}

func (m *userManager) EnsureUser(principalName, displayName string) (*v3.User, error) {
	// First check the local cache
	u, err := m.checkCache(principalName)
	if err != nil {
		return nil, err
	}
	if u != nil {
		return u, nil
	}

	// Not in cache, query API by label
	u, labelSet, err := m.checkLabels(principalName)
	if err != nil {
		return nil, err
	}
	if u != nil {
		return u, nil
	}

	// Doesn't exist, create user
	user := &v3.User{
		ObjectMeta: v1.ObjectMeta{
			GenerateName: "user-",
			Labels:       labelSet,
		},
		DisplayName:  displayName,
		PrincipalIDs: []string{principalName},
	}

	created, err := m.users.Create(user)
	if err != nil {
		return nil, err
	}

	_, err = m.globalRoleBindings.Create(&v3.GlobalRoleBinding{
		ObjectMeta: v1.ObjectMeta{
			GenerateName: "globalrolebinding-",
		},
		UserName:       created.Name,
		GlobalRoleName: "user",
	})
	if err != nil {
		return nil, err
	}

	localPrincipal := "local://" + created.Name
	if !slice.ContainsString(created.PrincipalIDs, localPrincipal) {
		created.PrincipalIDs = append(created.PrincipalIDs, localPrincipal)
		return m.users.Update(created)
	}

	return created, nil
}

func (m *userManager) checkCache(principalName string) (*v3.User, error) {
	users, err := m.userIndexer.ByIndex(userByPrincipalIndex, principalName)
	if err != nil {
		return nil, err
	}
	if len(users) > 1 {
		return nil, errors.Errorf("can't find unique user for principal %v", principalName)
	}
	if len(users) == 1 {
		u := users[0].(*v3.User)
		return u.DeepCopy(), nil
	}
	return nil, nil
}

func (m *userManager) checkLabels(principalName string) (*v3.User, labels.Set, error) {
	encodedPrincipalID := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString([]byte(principalName))
	if len(encodedPrincipalID) > 63 {
		encodedPrincipalID = encodedPrincipalID[:63]
	}
	set := labels.Set(map[string]string{encodedPrincipalID: "hashed-principal-name"})
	users, err := m.users.List(v1.ListOptions{LabelSelector: set.String()})
	if err != nil {
		return nil, nil, err
	}

	if len(users.Items) == 0 {
		return nil, set, nil
	}

	var match *v3.User
	for _, u := range users.Items {
		if slice.ContainsString(u.PrincipalIDs, principalName) {
			if match != nil {
				// error out on duplicates
				return nil, nil, errors.Errorf("can't find unique user for principal %v", principalName)
			}
			match = &u
		}
	}

	return match, set, nil
}

func userByPrincipal(obj interface{}) ([]string, error) {
	u, ok := obj.(*v3.User)
	if !ok {
		return []string{}, nil
	}

	return u.PrincipalIDs, nil
}
<commit_msg>Account for group membership in restricted mode<commit_after>package common

import (
	"encoding/base32"

	"github.com/pkg/errors"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/slice"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/rancher/types/user"
	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/tools/cache"
)

const (
	userAuthHeader         = "Impersonate-User"
	userByPrincipalIndex   = "auth.management.cattle.io/userByPrincipal"
	groupPrincpalCRTBIndex = "auth.management.cattle.io/groupPrincipalCRTB"
	groupPrincpalPRTBIndex = "auth.management.cattle.io/groupPrincipalPRTB"
)

func NewUserManager(scaledContext *config.ScaledContext) (user.Manager, error) {
	userInformer := scaledContext.Management.Users("").Controller().Informer()
	userIndexers := map[string]cache.IndexFunc{
		userByPrincipalIndex: userByPrincipal,
	}
	if err := userInformer.AddIndexers(userIndexers); err != nil {
		return nil, err
	}

	crtbInformer := scaledContext.Management.ClusterRoleTemplateBindings("").Controller().Informer()
	crtbIndexers := map[string]cache.IndexFunc{
		groupPrincpalCRTBIndex: groupPrincipalCRTB,
	}
	if err := crtbInformer.AddIndexers(crtbIndexers); err != nil {
		return nil, err
	}

	prtbInformer := scaledContext.Management.ProjectRoleTemplateBindings("").Controller().Informer()
	prtbIndexers := map[string]cache.IndexFunc{
		groupPrincpalPRTBIndex: groupPrincipalPRTB,
	}
	if err := prtbInformer.AddIndexers(prtbIndexers); err != nil {
		return nil, err
	}

	return &userManager{
		users:              scaledContext.Management.Users(""),
		userIndexer:        userInformer.GetIndexer(),
		crtbIndexer:        crtbInformer.GetIndexer(),
		prtbIndexer:        prtbInformer.GetIndexer(),
		globalRoleBindings: scaledContext.Management.GlobalRoleBindings(""),
	}, nil
}

type userManager struct {
	users              v3.UserInterface
	globalRoleBindings v3.GlobalRoleBindingInterface
	userIndexer        cache.Indexer
	crtbIndexer        cache.Indexer
	prtbIndexer        cache.Indexer
}

func (m *userManager) SetPrincipalOnCurrentUser(apiContext *types.APIContext, principal v3.Principal) (*v3.User, error) {
	userID := m.GetUser(apiContext)
	if userID == "" {
		return nil, errors.New("user not provided")
	}

	user, err := m.users.Get(userID, v1.GetOptions{})
	if err != nil {
		return nil, err
	}

	if !slice.ContainsString(user.PrincipalIDs, principal.Name) {
		user.PrincipalIDs = append(user.PrincipalIDs, principal.Name)
		return m.users.Update(user)
	}
	return user, nil
}

func (m *userManager) GetUser(apiContext *types.APIContext) string {
	return apiContext.Request.Header.Get(userAuthHeader)
}

// checkis if the supplied principal can login based on the accessMode and allowed principals
func (m *userManager) CheckAccess(accessMode string, allowedPrincipalIDs []string, userPrinc v3.Principal, groups []v3.Principal) (bool, error) {
	if accessMode == "unrestricted" {
		return true, nil
	}

	if accessMode == "required" || accessMode == "restricted" {
		if slice.ContainsString(allowedPrincipalIDs, userPrinc.Name) {
			return true, nil
		}
		for _, g := range groups {
			if slice.ContainsString(allowedPrincipalIDs, g.Name) {
				return true, nil
			}
		}
		if accessMode == "restricted" {
			// check if any of the groups principals have been assigned to clusters or projects
			allowed, err := m.hasMemberGroup(groups)
			if err != nil {
				return false, err
			}
			if allowed {
				return true, nil
			}

			// check if user prinicpal exists as a user
			u, err := m.checkCache(userPrinc.Name)
			if err != nil {
				return false, err
			}
			if u != nil {
				return true, nil
			}

			// user principal not in cache, query API by label
			u, _, err = m.checkLabels(userPrinc.Name)
			if err != nil {
				return false, err
			}
			if u != nil {
				return true, nil
			}
		}
		return false, nil
	}
	return false, errors.Errorf("Unsupport accessMode: %v", accessMode)
}

func (m *userManager) EnsureUser(principalName, displayName string) (*v3.User, error) {
	// First check the local cache
	u, err := m.checkCache(principalName)
	if err != nil {
		return nil, err
	}
	if u != nil {
		return u, nil
	}

	// Not in cache, query API by label
	u, labelSet, err := m.checkLabels(principalName)
	if err != nil {
		return nil, err
	}
	if u != nil {
		return u, nil
	}

	// Doesn't exist, create user
	user := &v3.User{
		ObjectMeta: v1.ObjectMeta{
			GenerateName: "user-",
			Labels:       labelSet,
		},
		DisplayName:  displayName,
		PrincipalIDs: []string{principalName},
	}

	created, err := m.users.Create(user)
	if err != nil {
		return nil, err
	}

	_, err = m.globalRoleBindings.Create(&v3.GlobalRoleBinding{
		ObjectMeta: v1.ObjectMeta{
			GenerateName: "globalrolebinding-",
		},
		UserName:       created.Name,
		GlobalRoleName: "user",
	})
	if err != nil {
		return nil, err
	}

	localPrincipal := "local://" + created.Name
	if !slice.ContainsString(created.PrincipalIDs, localPrincipal) {
		created.PrincipalIDs = append(created.PrincipalIDs, localPrincipal)
		return m.users.Update(created)
	}

	return created, nil
}

func (m *userManager) checkCache(principalName string) (*v3.User, error) {
	users, err := m.userIndexer.ByIndex(userByPrincipalIndex, principalName)
	if err != nil {
		return nil, err
	}
	if len(users) > 1 {
		return nil, errors.Errorf("can't find unique user for principal %v", principalName)
	}
	if len(users) == 1 {
		u := users[0].(*v3.User)
		return u.DeepCopy(), nil
	}
	return nil, nil
}

func (m *userManager) hasMemberGroup(groupPrincipals []v3.Principal) (bool, error) {
	for _, g := range groupPrincipals {
		crtbs, err := m.crtbIndexer.ByIndex(groupPrincpalCRTBIndex, g.Name)
		if err != nil {
			return false, err
		}
		if len(crtbs) > 0 {
			return true, nil
		}
		prtbs, err := m.prtbIndexer.ByIndex(groupPrincpalPRTBIndex, g.Name)
		if err != nil {
			return false, err
		}
		if len(prtbs) > 0 {
			return true, nil
		}
	}

	return false, nil
}

func (m *userManager) checkLabels(principalName string) (*v3.User, labels.Set, error) {
	encodedPrincipalID := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString([]byte(principalName))
	if len(encodedPrincipalID) > 63 {
		encodedPrincipalID = encodedPrincipalID[:63]
	}
	set := labels.Set(map[string]string{encodedPrincipalID: "hashed-principal-name"})
	users, err := m.users.List(v1.ListOptions{LabelSelector: set.String()})
	if err != nil {
		return nil, nil, err
	}

	if len(users.Items) == 0 {
		return nil, set, nil
	}

	var match *v3.User
	for _, u := range users.Items {
		if slice.ContainsString(u.PrincipalIDs, principalName) {
			if match != nil {
				// error out on duplicates
				return nil, nil, errors.Errorf("can't find unique user for principal %v", principalName)
			}
			match = &u
		}
	}

	return match, set, nil
}

func userByPrincipal(obj interface{}) ([]string, error) {
	u, ok := obj.(*v3.User)
	if !ok {
		return []string{}, nil
	}

	return u.PrincipalIDs, nil
}

func groupPrincipalCRTB(obj interface{}) ([]string, error) {
	var gp []string
	b, ok := obj.(*v3.ClusterRoleTemplateBinding)
	if !ok {
		return []string{}, nil
	}
	if b.GroupPrincipalName != "" {
		gp = append(gp, b.GroupPrincipalName)
	}
	return gp, nil
}

func groupPrincipalPRTB(obj interface{}) ([]string, error) {
	var gp []string
	b, ok := obj.(*v3.ProjectRoleTemplateBinding)
	if !ok {
		return []string{}, nil
	}
	if b.GroupPrincipalName != "" {
		gp = append(gp, b.GroupPrincipalName)
	}
	return gp, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"

	"collectd.org/cdtime"
)

// jsonValueList represents the format used by collectd's JSON export.
type jsonValueList struct {
	Values         []json.Number `json:"values"`
	DSTypes        []string      `json:"dstypes"`
	DSNames        []string      `json:"dsnames,omitempty"`
	Time           cdtime.Time   `json:"time"`
	Interval       cdtime.Time   `json:"interval"`
	Host           string        `json:"host"`
	Plugin         string        `json:"plugin"`
	PluginInstance string        `json:"plugin_instance,omitempty"`
	Type           string        `json:"type"`
	TypeInstance   string        `json:"type_instance,omitempty"`
}

// MarshalJSON implements the "encoding/json".Marshaler interface for
// ValueList.
func (vl ValueList) MarshalJSON() ([]byte, error) {
	jvl := jsonValueList{
		Values:         make([]json.Number, len(vl.Values)),
		DSTypes:        make([]string, len(vl.Values)),
		Time:           cdtime.New(vl.Time),
		Interval:       cdtime.NewDuration(vl.Interval),
		Host:           vl.Host,
		Plugin:         vl.Plugin,
		PluginInstance: vl.PluginInstance,
		Type:           vl.Type,
		TypeInstance:   vl.TypeInstance,
	}

	for i, v := range vl.Values {
		switch v := v.(type) {
		case Gauge:
			jvl.Values[i] = json.Number(fmt.Sprintf("%g", v))
		case Derive, Counter:
			jvl.Values[i] = json.Number(fmt.Sprintf("%d", v))
		default:
			return nil, fmt.Errorf("unexpected data source type: %T", v)
		}
		jvl.DSTypes[i] = v.Type()
	}

	if len(vl.Values) == 1 {
		jvl.DSNames = []string{"value"}
	}

	return json.Marshal(jvl)
}

// UnmarshalJSON implements the "encoding/json".Unmarshaler interface for
// ValueList.
func (vl *ValueList) UnmarshalJSON(data []byte) error {
	var jvl jsonValueList

	if err := json.Unmarshal(data, &jvl); err != nil {
		return err
	}

	vl.Host = jvl.Host
	vl.Plugin = jvl.Plugin
	vl.PluginInstance = jvl.PluginInstance
	vl.Type = jvl.Type
	vl.TypeInstance = jvl.TypeInstance

	vl.Time = jvl.Time.Time()
	vl.Interval = jvl.Interval.Duration()
	vl.Values = make([]Value, len(jvl.Values))

	if len(jvl.Values) != len(jvl.DSTypes) {
		return fmt.Errorf("invalid data: %d value(s), %d data source type(s)",
			len(jvl.Values), len(jvl.DSTypes))
	}

	for i, n := range jvl.Values {
		switch jvl.DSTypes[i] {
		case "gauge":
			v, err := n.Float64()
			if err != nil {
				return err
			}
			vl.Values[i] = Gauge(v)
		case "derive":
			v, err := n.Int64()
			if err != nil {
				return err
			}
			vl.Values[i] = Derive(v)
		case "counter":
			v, err := n.Int64()
			if err != nil {
				return err
			}
			vl.Values[i] = Counter(v)
		default:
			return fmt.Errorf("unexpected data source type: %q", jvl.DSTypes[i])
		}
	}

	return nil
}
<commit_msg>api: Document problems with write_http's "StoreRates" option.<commit_after>package api

import (
	"encoding/json"
	"fmt"

	"collectd.org/cdtime"
)

// jsonValueList represents the format used by collectd's JSON export.
type jsonValueList struct {
	Values         []json.Number `json:"values"`
	DSTypes        []string      `json:"dstypes"`
	DSNames        []string      `json:"dsnames,omitempty"`
	Time           cdtime.Time   `json:"time"`
	Interval       cdtime.Time   `json:"interval"`
	Host           string        `json:"host"`
	Plugin         string        `json:"plugin"`
	PluginInstance string        `json:"plugin_instance,omitempty"`
	Type           string        `json:"type"`
	TypeInstance   string        `json:"type_instance,omitempty"`
}

// MarshalJSON implements the "encoding/json".Marshaler interface for
// ValueList.
func (vl ValueList) MarshalJSON() ([]byte, error) {
	jvl := jsonValueList{
		Values:         make([]json.Number, len(vl.Values)),
		DSTypes:        make([]string, len(vl.Values)),
		Time:           cdtime.New(vl.Time),
		Interval:       cdtime.NewDuration(vl.Interval),
		Host:           vl.Host,
		Plugin:         vl.Plugin,
		PluginInstance: vl.PluginInstance,
		Type:           vl.Type,
		TypeInstance:   vl.TypeInstance,
	}

	for i, v := range vl.Values {
		switch v := v.(type) {
		case Gauge:
			jvl.Values[i] = json.Number(fmt.Sprintf("%g", v))
		case Derive, Counter:
			jvl.Values[i] = json.Number(fmt.Sprintf("%d", v))
		default:
			return nil, fmt.Errorf("unexpected data source type: %T", v)
		}
		jvl.DSTypes[i] = v.Type()
	}

	if len(vl.Values) == 1 {
		jvl.DSNames = []string{"value"}
	}

	return json.Marshal(jvl)
}

// UnmarshalJSON implements the "encoding/json".Unmarshaler interface for
// ValueList.
//
// Please note that this function is currently not compatible with write_http's
// "StoreRates" setting: if enabled, write_http converts derives and counters
// to a rate (a floating point number), but still puts "derive" or "counter" in
// the "dstypes" array. UnmarshalJSON will try to parse such values as
// integers, which will fail in many cases.
func (vl *ValueList) UnmarshalJSON(data []byte) error {
	var jvl jsonValueList

	if err := json.Unmarshal(data, &jvl); err != nil {
		return err
	}

	vl.Host = jvl.Host
	vl.Plugin = jvl.Plugin
	vl.PluginInstance = jvl.PluginInstance
	vl.Type = jvl.Type
	vl.TypeInstance = jvl.TypeInstance

	vl.Time = jvl.Time.Time()
	vl.Interval = jvl.Interval.Duration()
	vl.Values = make([]Value, len(jvl.Values))

	if len(jvl.Values) != len(jvl.DSTypes) {
		return fmt.Errorf("invalid data: %d value(s), %d data source type(s)",
			len(jvl.Values), len(jvl.DSTypes))
	}

	for i, n := range jvl.Values {
		switch jvl.DSTypes[i] {
		case "gauge":
			v, err := n.Float64()
			if err != nil {
				return err
			}
			vl.Values[i] = Gauge(v)
		case "derive":
			v, err := n.Int64()
			if err != nil {
				return err
			}
			vl.Values[i] = Derive(v)
		case "counter":
			v, err := n.Int64()
			if err != nil {
				return err
			}
			vl.Values[i] = Counter(v)
		default:
			return fmt.Errorf("unexpected data source type: %q", jvl.DSTypes[i])
		}
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Camlistore 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 blobpacked

import (
	"bytes"
	"reflect"
	"strconv"
	"testing"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/blobserver"
	"camlistore.org/pkg/blobserver/storagetest"
	"camlistore.org/pkg/context"
	"camlistore.org/pkg/schema"
	"camlistore.org/pkg/sorted"
	"camlistore.org/pkg/test"
)

func TestStreamBlobs(t *testing.T) {
	small := new(test.Fetcher)
	s := &storage{
		small: small,
		large: new(test.Fetcher),
		meta:  sorted.NewMemoryKeyValue(),
		log:   test.NewLogger(t, "blobpacked: "),
	}
	s.init()

	all := map[blob.Ref]bool{}
	const nBlobs = 10
	for i := 0; i < nBlobs; i++ {
		b := &test.Blob{strconv.Itoa(i)}
		b.MustUpload(t, small)
		all[b.BlobRef()] = true
	}
	ctx := context.New()
	defer ctx.Cancel()
	token := "" // beginning

	got := map[blob.Ref]bool{}
	dest := make(chan blobserver.BlobAndToken, 16)
	done := make(chan bool)
	go func() {
		defer close(done)
		for bt := range dest {
			got[bt.Blob.Ref()] = true
		}
	}()
	err := s.StreamBlobs(ctx, dest, token)
	if err != nil {
		t.Fatalf("StreamBlobs = %v", err)
	}
	<-done
	if !reflect.DeepEqual(got, all) {
		t.Errorf("Got blobs %v; want %v", got, all)
	}
	storagetest.TestStreamer(t, s, storagetest.WantN(nBlobs))
}

type subfetcherStorage interface {
	blobserver.Storage
	blob.SubFetcher
}

func enumStore() subfetcherStorage {
	// Hide the BlobStreamer (and any other) interface impl beyond
	// just the blobserver.Storage base (which has Enumerator, but not Streamer)
	return struct{ subfetcherStorage }{new(test.Fetcher)}
}

type streamerStorage interface {
	subfetcherStorage
	blobserver.BlobStreamer
}

func streamStore() streamerStorage {
	return new(test.Fetcher)
}

func TestStreamBlobs_Loose_Enumerate(t *testing.T) {
	testStreamBlobs(t, enumStore(), enumStore() /* unused */, populateLoose)
}

func TestStreamBlobs_Loose_Streamed(t *testing.T) {
	testStreamBlobs(t, streamStore(), enumStore() /* unused */, populateLoose)
}

func TestStreamBlobs_Packed_Enumerate(t *testing.T) {
	testStreamBlobs(t, enumStore(), enumStore(), populatePacked)
}

func TestStreamBlobs_Packed_Streamed(t *testing.T) {
	testStreamBlobs(t, streamStore(), streamStore(), populatePacked)
}

func testStreamBlobs(t *testing.T,
	small blobserver.Storage,
	large subFetcherStorage,
	populate func(*testing.T, *storage) []storagetest.StreamerTestOpt) {
	s := &storage{
		small: small,
		large: large,
		meta:  sorted.NewMemoryKeyValue(),
		log:   test.NewLogger(t, "blobpacked: "),
	}
	s.init()
	wants := populate(t, s)
	storagetest.TestStreamer(t, s, wants...)
}

func populateLoose(t *testing.T, s *storage) (wants []storagetest.StreamerTestOpt) {
	const nBlobs = 10
	for i := 0; i < nBlobs; i++ {
		(&test.Blob{strconv.Itoa(i)}).MustUpload(t, s)
	}
	return append(wants, storagetest.WantN(nBlobs))
}

func populatePacked(t *testing.T, s *storage) (wants []storagetest.StreamerTestOpt) {
	const fileSize = 5 << 20
	const fileName = "foo.dat"
	fileContents := randBytes(fileSize)
	_, err := schema.WriteFileFromReader(s, fileName, bytes.NewReader(fileContents))
	if err != nil {
		t.Fatalf("WriteFileFromReader: %v", err)
	}
	return nil
}
<commit_msg>blobserver/blobpacked: add test of stream working with 2 zips<commit_after>/*
Copyright 2014 The Camlistore 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 blobpacked

import (
	"bytes"
	"reflect"
	"strconv"
	"testing"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/blobserver"
	"camlistore.org/pkg/blobserver/storagetest"
	"camlistore.org/pkg/context"
	"camlistore.org/pkg/schema"
	"camlistore.org/pkg/sorted"
	"camlistore.org/pkg/test"
)

func TestStreamBlobs(t *testing.T) {
	small := new(test.Fetcher)
	s := &storage{
		small: small,
		large: new(test.Fetcher),
		meta:  sorted.NewMemoryKeyValue(),
		log:   test.NewLogger(t, "blobpacked: "),
	}
	s.init()

	all := map[blob.Ref]bool{}
	const nBlobs = 10
	for i := 0; i < nBlobs; i++ {
		b := &test.Blob{strconv.Itoa(i)}
		b.MustUpload(t, small)
		all[b.BlobRef()] = true
	}
	ctx := context.New()
	defer ctx.Cancel()
	token := "" // beginning

	got := map[blob.Ref]bool{}
	dest := make(chan blobserver.BlobAndToken, 16)
	done := make(chan bool)
	go func() {
		defer close(done)
		for bt := range dest {
			got[bt.Blob.Ref()] = true
		}
	}()
	err := s.StreamBlobs(ctx, dest, token)
	if err != nil {
		t.Fatalf("StreamBlobs = %v", err)
	}
	<-done
	if !reflect.DeepEqual(got, all) {
		t.Errorf("Got blobs %v; want %v", got, all)
	}
	storagetest.TestStreamer(t, s, storagetest.WantN(nBlobs))
}

type subfetcherStorage interface {
	blobserver.Storage
	blob.SubFetcher
}

func enumStore() subfetcherStorage {
	// Hide the BlobStreamer (and any other) interface impl beyond
	// just the blobserver.Storage base (which has Enumerator, but not Streamer)
	return struct{ subfetcherStorage }{new(test.Fetcher)}
}

type streamerStorage interface {
	subfetcherStorage
	blobserver.BlobStreamer
}

func streamStore() streamerStorage {
	return new(test.Fetcher)
}

func TestStreamBlobs_Loose_Enumerate(t *testing.T) {
	testStreamBlobs(t, enumStore(), enumStore() /* unused */, populateLoose)
}

func TestStreamBlobs_Loose_Streamed(t *testing.T) {
	testStreamBlobs(t, streamStore(), enumStore() /* unused */, populateLoose)
}

func TestStreamBlobs_Packed_Enumerate(t *testing.T) {
	testStreamBlobs(t, enumStore(), enumStore(), populatePacked)
}

func TestStreamBlobs_Packed_Streamed(t *testing.T) {
	testStreamBlobs(t, streamStore(), streamStore(), populatePacked)
}

// 2 packed files
func TestStreamBlobs_Packed2_Streamed(t *testing.T) {
	testStreamBlobs(t, streamStore(), streamStore(), populatePacked2)
}

func testStreamBlobs(t *testing.T,
	small blobserver.Storage,
	large subFetcherStorage,
	populate func(*testing.T, *storage) []storagetest.StreamerTestOpt) {
	s := &storage{
		small: small,
		large: large,
		meta:  sorted.NewMemoryKeyValue(),
		log:   test.NewLogger(t, "blobpacked: "),
	}
	s.init()
	wants := populate(t, s)
	storagetest.TestStreamer(t, s, wants...)
}

func populateLoose(t *testing.T, s *storage) (wants []storagetest.StreamerTestOpt) {
	const nBlobs = 10
	for i := 0; i < nBlobs; i++ {
		(&test.Blob{strconv.Itoa(i)}).MustUpload(t, s)
	}
	return append(wants, storagetest.WantN(nBlobs))
}

func populatePacked(t *testing.T, s *storage) (wants []storagetest.StreamerTestOpt) {
	const fileSize = 5 << 20
	const fileName = "foo.dat"
	fileContents := randBytes(fileSize)
	_, err := schema.WriteFileFromReader(s, fileName, bytes.NewReader(fileContents))
	if err != nil {
		t.Fatalf("WriteFileFromReader: %v", err)
	}
	return nil
}

func populatePacked2(t *testing.T, s *storage) (wants []storagetest.StreamerTestOpt) {
	const fileSize = 1 << 20
	data := randBytes(fileSize)
	_, err := schema.WriteFileFromReader(s, "first-half.dat", bytes.NewReader(data[:fileSize/2]))
	if err != nil {
		t.Fatalf("WriteFileFromReader: %v", err)
	}
	_, err = schema.WriteFileFromReader(s, "second-half.dat", bytes.NewReader(data[fileSize/2:]))
	if err != nil {
		t.Fatalf("WriteFileFromReader: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"text/template"

	"github.com/cgansen/elastigo/api"
	"github.com/cgansen/health-near-me/healthnearme"
	geo "github.com/kellydunn/golang-geo"
)

var tmplPath string

func init() {
	flag.StringVar(&tmplPath, "tmpl", "../tmpl/", "path to templates")
	flag.Parse()
}

// Perform a search for a SMS user.
func SMSSearchHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s %s %s", req.Method, req.RequestURI, req.URL.RawQuery, req.Header.Get("User-Agent"))

	if err := req.ParseForm(); err != nil {
		log.Printf("error parsing form: %s", err)
		http.Error(w, "error parsing form body", 500)
		return
	}

	// TODO(cgansen):
	// support sessions
	// search regex

	search := req.FormValue("Body")
	log.Printf("sms search: %s", search)

	cmd := strings.TrimSpace(strings.ToLower(search))
	switch cmd {
	case "list", "list services":
		t, err := template.ParseFiles(tmplPath + "help.txt")
		if err != nil {
			log.Printf("error loading template: %s", err)
			http.Error(w, "error loading template", 500)
			return
		}

		if err := t.Execute(w, nil); err != nil {
			log.Printf("error executing template: %s", err)
			http.Error(w, "error executing template", 500)
			return
		}

		return
	default:
		// split query
		pieces := strings.Split(cmd, "near")

		// term := strings.TrimSpace(pieces[0])
		location := strings.TrimSpace(pieces[1])

		// geocode
		geocoder := &geo.GoogleGeocoder{}
		point, err := geocoder.Geocode(pieces[1])
		if err != nil {
			// handle
			log.Printf("error geocoding: %s, location is: %s", err, location)
			http.Error(w, "error geocoding", 500)
			return
		}

		log.Printf("geocoded %s to %#v", location, point)

		// TODO map term to searchType

		// lookup
		result, err := healthnearme.DoSearch(point.Lat(), point.Lng(), 1609, "all")

		// respond
		hits, err := healthnearme.LoadResults(result, point)
		if err != nil {
			log.Print(err)
			http.Error(w, "error processing search results", 500)
			return
		}
		log.Printf("%d results for %s", len(hits), cmd)

		t, err := template.New("nearby_providers.txt").Funcs(template.FuncMap{"round": strconv.FormatFloat}).ParseFiles(tmplPath + "nearby_providers.txt")
		if err != nil {
			log.Print("template error: ", err)
			http.Error(w, "error loading template", 500)
			return
		}

		ctxt := map[string]interface{}{
			"Count":    len(hits),
			"Location": location,
			"Results":  hits,
		}

		if err := t.Execute(w, ctxt); err != nil {
			log.Print(err)
			http.Error(w, "error writing results", 500)
			return
		}

		w.Header().Add("Content-type", "text/xml")
		return
	}

	return
}

func SearchHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s %s %s", req.Method, req.RequestURI, req.RemoteAddr, req.Header.Get("User-Agent"))

	slat, slon, sdist, styp := req.FormValue("lat"), req.FormValue("lon"), req.FormValue("dist"), req.FormValue("searchType")

	lat, err := strconv.ParseFloat(slat, 64)
	if err != nil {
		http.Error(w, "lat is required and must be a float, e.g. 41.42", 400)
		return
	}

	lon, err := strconv.ParseFloat(slon, 64)
	if err != nil {
		http.Error(w, "lon is required and must be a float, e.g. -87.88", 400)
		return
	}

	dist, err := strconv.ParseInt(sdist, 10, 64)
	if err != nil {
		http.Error(w, "dist is required and must be an integer", 400)
		return
	}

	if styp == "" {
		http.Error(w, "searchType is required and must be an integer or 'all'", 400)
		return
	}

        log.Printf("http search: %f,%f %d %s", lat, lon, dist, styp)

	result, err := healthnearme.DoSearch(lat, lon, dist, styp)
	if err != nil {
		log.Printf("error searching: %s", err)
		http.Error(w, "error searching index", 503)
		return
	}

	hits, err := healthnearme.LoadResults(result, geo.NewPoint(lat, lon))
	if err != nil {
		log.Print(err)
		http.Error(w, "error processing search results", 500)
	}

	jsn, err := json.MarshalIndent(hits, "", "  ")
	if err != nil {
		log.Print(err)
		http.Error(w, "error dumping search results to json", 500)
		return
	}

	w.Header().Add("Content-type", "application/json")
	// delim := ")]}',\n"
	delim := ""
	resp := fmt.Sprintf("%s%s(%s);", delim, req.FormValue("callback"), string(jsn))

	_, err = w.Write([]byte(resp))
	return

}

func HealthCheckHandler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("OK"))
	return
}

func main() {
	api.Domain = "localhost"

	http.HandleFunc("/sms_search", SMSSearchHandler)
	http.HandleFunc("/search", SearchHandler)
	http.HandleFunc("/healthcheck", HealthCheckHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>catch IP forwarding<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"text/template"

	"github.com/cgansen/elastigo/api"
	"github.com/cgansen/health-near-me/healthnearme"
	geo "github.com/kellydunn/golang-geo"
)

var tmplPath string

func init() {
	flag.StringVar(&tmplPath, "tmpl", "../tmpl/", "path to templates")
	flag.Parse()
}

// Perform a search for a SMS user.
func SMSSearchHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s %s %s", req.Method, req.RequestURI, req.URL.RawQuery, req.Header.Get("User-Agent"))

	if err := req.ParseForm(); err != nil {
		log.Printf("error parsing form: %s", err)
		http.Error(w, "error parsing form body", 500)
		return
	}

	// TODO(cgansen):
	// support sessions
	// search regex

	search := req.FormValue("Body")
	log.Printf("sms search: %s", search)

	cmd := strings.TrimSpace(strings.ToLower(search))
	switch cmd {
	case "list", "list services":
		t, err := template.ParseFiles(tmplPath + "help.txt")
		if err != nil {
			log.Printf("error loading template: %s", err)
			http.Error(w, "error loading template", 500)
			return
		}

		if err := t.Execute(w, nil); err != nil {
			log.Printf("error executing template: %s", err)
			http.Error(w, "error executing template", 500)
			return
		}

		return
	default:
		// split query
		pieces := strings.Split(cmd, "near")

		// term := strings.TrimSpace(pieces[0])
		location := strings.TrimSpace(pieces[1])

		// geocode
		geocoder := &geo.GoogleGeocoder{}
		point, err := geocoder.Geocode(pieces[1])
		if err != nil {
			// handle
			log.Printf("error geocoding: %s, location is: %s", err, location)
			http.Error(w, "error geocoding", 500)
			return
		}

		log.Printf("geocoded %s to %#v", location, point)

		// TODO map term to searchType

		// lookup
		result, err := healthnearme.DoSearch(point.Lat(), point.Lng(), 1609, "all")

		// respond
		hits, err := healthnearme.LoadResults(result, point)
		if err != nil {
			log.Print(err)
			http.Error(w, "error processing search results", 500)
			return
		}
		log.Printf("%d results for %s", len(hits), cmd)

		t, err := template.New("nearby_providers.txt").Funcs(template.FuncMap{"round": strconv.FormatFloat}).ParseFiles(tmplPath + "nearby_providers.txt")
		if err != nil {
			log.Print("template error: ", err)
			http.Error(w, "error loading template", 500)
			return
		}

		ctxt := map[string]interface{}{
			"Count":    len(hits),
			"Location": location,
			"Results":  hits,
		}

		if err := t.Execute(w, ctxt); err != nil {
			log.Print(err)
			http.Error(w, "error writing results", 500)
			return
		}

		w.Header().Add("Content-type", "text/xml")
		return
	}

	return
}

func SearchHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s %s %s %s", req.Method, req.RequestURI, req.RemoteAddr, req.Header.Get("X-Real-IP"), req.Header.Get("User-Agent"))

	slat, slon, sdist, styp := req.FormValue("lat"), req.FormValue("lon"), req.FormValue("dist"), req.FormValue("searchType")

	lat, err := strconv.ParseFloat(slat, 64)
	if err != nil {
		http.Error(w, "lat is required and must be a float, e.g. 41.42", 400)
		return
	}

	lon, err := strconv.ParseFloat(slon, 64)
	if err != nil {
		http.Error(w, "lon is required and must be a float, e.g. -87.88", 400)
		return
	}

	dist, err := strconv.ParseInt(sdist, 10, 64)
	if err != nil {
		http.Error(w, "dist is required and must be an integer", 400)
		return
	}

	if styp == "" {
		http.Error(w, "searchType is required and must be an integer or 'all'", 400)
		return
	}

        log.Printf("http search: %f,%f %d %s", lat, lon, dist, styp)

	result, err := healthnearme.DoSearch(lat, lon, dist, styp)
	if err != nil {
		log.Printf("error searching: %s", err)
		http.Error(w, "error searching index", 503)
		return
	}

	hits, err := healthnearme.LoadResults(result, geo.NewPoint(lat, lon))
	if err != nil {
		log.Print(err)
		http.Error(w, "error processing search results", 500)
	}

	jsn, err := json.MarshalIndent(hits, "", "  ")
	if err != nil {
		log.Print(err)
		http.Error(w, "error dumping search results to json", 500)
		return
	}

	w.Header().Add("Content-type", "application/json")
	// delim := ")]}',\n"
	delim := ""
	resp := fmt.Sprintf("%s%s(%s);", delim, req.FormValue("callback"), string(jsn))

	_, err = w.Write([]byte(resp))
	return

}

func HealthCheckHandler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("OK"))
	return
}

func main() {
	api.Domain = "localhost"

	http.HandleFunc("/sms_search", SMSSearchHandler)
	http.HandleFunc("/search", SearchHandler)
	http.HandleFunc("/healthcheck", HealthCheckHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 localpath

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"k8s.io/client-go/util/homedir"
)

func TestReplaceWinDriveLetterToVolumeName(t *testing.T) {
	path, err := ioutil.TempDir("", "repwindl2vn")
	if err != nil {
		t.Fatalf("Error make tmp directory: %v", err)
	}
	defer os.RemoveAll(path)

	if runtime.GOOS != "windows" {
		// Replace to fake func.
		getWindowsVolumeName = func(d string) (string, error) {
			return `/`, nil
		}
		// Add dummy Windows drive letter.
		path = `C:` + path
	}

	if _, err := replaceWinDriveLetterToVolumeName(path); err != nil {
		t.Errorf("Error replace a Windows drive letter to a volume name: %v", err)
	}
}

func TestHasWindowsDriveLetter(t *testing.T) {
	cases := []struct {
		path string
		want bool
	}{
		{`C:\Users\Foo\.minikube`, true},
		{`D:\minikube\.minikube`, true},
		{`C\Foo\Bar\.minikube`, false},
		{`/home/foo/.minikube`, false},
	}

	for _, tc := range cases {
		if hasWindowsDriveLetter(tc.path) != tc.want {
			t.Errorf("%s have a Windows drive letter: %t", tc.path, tc.want)
		}
	}
}

func TestMiniPath(t *testing.T) {
	var testCases = []struct {
		env, basePath string
	}{
		{"/tmp/.minikube", "/tmp/"},
		{"/tmp/", "/tmp"},
		{"", homedir.HomeDir()},
	}
	for _, tc := range testCases {
		originalEnv := os.Getenv(MinikubeHome)
		defer func() { // revert to pre-test env var
			err := os.Setenv(MinikubeHome, originalEnv)
			if err != nil {
				t.Fatalf("Error reverting env %s to its original value (%s) var after test ", MinikubeHome, originalEnv)
			}
		}()
		t.Run(tc.env, func(t *testing.T) {
			expectedPath := filepath.Join(tc.basePath, ".minikube")
			os.Setenv(MinikubeHome, tc.env)
			path := MiniPath()
			if path != expectedPath {
				t.Errorf("MiniPath expected to return '%s', but got '%s'", expectedPath, path)
			}
		})
	}
}

func TestMachinePath(t *testing.T) {
	var testCases = []struct {
		miniHome []string
		contains string
	}{
		{[]string{"tmp", "foo", "bar", "baz"}, "tmp"},
		{[]string{"tmp"}, "tmp"},
		{[]string{}, MiniPath()},
	}
	for _, tc := range testCases {
		t.Run(fmt.Sprintf("%s", tc.miniHome), func(t *testing.T) {
			machinePath := MachinePath("foo", tc.miniHome...)
			if !strings.Contains(machinePath, tc.contains) {
				t.Errorf("Function MachinePath returned (%v) which doesn't contain expected (%v)", machinePath, tc.contains)
			}
		})
	}
}

type propertyFnWithArg func(string) string

func TestPropertyWithNameArg(t *testing.T) {
	var testCases = []struct {
		propertyFunc propertyFnWithArg
		name         string
	}{
		{Profile, "Profile"},
		{ClientCert, "ClientCert"},
		{ClientKey, "ClientKey"},
	}
	miniPath := MiniPath()
	mockedName := "foo"
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if !strings.Contains(tc.propertyFunc(mockedName), MiniPath()) {
				t.Errorf("Propert %s(%v) doesn't contain miniPat %v", tc.name, tc.propertyFunc, miniPath)
			}
			if !strings.Contains(tc.propertyFunc(mockedName), mockedName) {
				t.Errorf("Propert %s(%v) doesn't contain passed name inpath %v", tc.name, tc.propertyFunc, mockedName)
			}
		})

	}
}

type propertyFnWithoutArg func() string

func TestPropertyWithoutNameArg(t *testing.T) {
	var testCases = []struct {
		propertyFunc propertyFnWithoutArg
		name         string
	}{
		{ConfigFile, "ConfigFile"},
		{CACert, "CACert"},
	}
	miniPath := MiniPath()
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if !strings.Contains(tc.propertyFunc(), MiniPath()) {
				t.Errorf("Propert %s(%v) doesn't contain miniPat %v", tc.name, tc.propertyFunc, miniPath)
			}
		})
	}
}
<commit_msg>typofix<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 localpath

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"k8s.io/client-go/util/homedir"
)

func TestReplaceWinDriveLetterToVolumeName(t *testing.T) {
	path, err := ioutil.TempDir("", "repwindl2vn")
	if err != nil {
		t.Fatalf("Error make tmp directory: %v", err)
	}
	defer os.RemoveAll(path)

	if runtime.GOOS != "windows" {
		// Replace to fake func.
		getWindowsVolumeName = func(d string) (string, error) {
			return `/`, nil
		}
		// Add dummy Windows drive letter.
		path = `C:` + path
	}

	if _, err := replaceWinDriveLetterToVolumeName(path); err != nil {
		t.Errorf("Error replace a Windows drive letter to a volume name: %v", err)
	}
}

func TestHasWindowsDriveLetter(t *testing.T) {
	cases := []struct {
		path string
		want bool
	}{
		{`C:\Users\Foo\.minikube`, true},
		{`D:\minikube\.minikube`, true},
		{`C\Foo\Bar\.minikube`, false},
		{`/home/foo/.minikube`, false},
	}

	for _, tc := range cases {
		if hasWindowsDriveLetter(tc.path) != tc.want {
			t.Errorf("%s have a Windows drive letter: %t", tc.path, tc.want)
		}
	}
}

func TestMiniPath(t *testing.T) {
	var testCases = []struct {
		env, basePath string
	}{
		{"/tmp/.minikube", "/tmp/"},
		{"/tmp/", "/tmp"},
		{"", homedir.HomeDir()},
	}
	for _, tc := range testCases {
		originalEnv := os.Getenv(MinikubeHome)
		defer func() { // revert to pre-test env var
			err := os.Setenv(MinikubeHome, originalEnv)
			if err != nil {
				t.Fatalf("Error reverting env %s to its original value (%s) var after test ", MinikubeHome, originalEnv)
			}
		}()
		t.Run(tc.env, func(t *testing.T) {
			expectedPath := filepath.Join(tc.basePath, ".minikube")
			os.Setenv(MinikubeHome, tc.env)
			path := MiniPath()
			if path != expectedPath {
				t.Errorf("MiniPath expected to return '%s', but got '%s'", expectedPath, path)
			}
		})
	}
}

func TestMachinePath(t *testing.T) {
	var testCases = []struct {
		miniHome []string
		contains string
	}{
		{[]string{"tmp", "foo", "bar", "baz"}, "tmp"},
		{[]string{"tmp"}, "tmp"},
		{[]string{}, MiniPath()},
	}
	for _, tc := range testCases {
		t.Run(fmt.Sprintf("%s", tc.miniHome), func(t *testing.T) {
			machinePath := MachinePath("foo", tc.miniHome...)
			if !strings.Contains(machinePath, tc.contains) {
				t.Errorf("Function MachinePath returned (%v) which doesn't contain expected (%v)", machinePath, tc.contains)
			}
		})
	}
}

type propertyFnWithArg func(string) string

func TestPropertyWithNameArg(t *testing.T) {
	var testCases = []struct {
		propertyFunc propertyFnWithArg
		name         string
	}{
		{Profile, "Profile"},
		{ClientCert, "ClientCert"},
		{ClientKey, "ClientKey"},
	}
	miniPath := MiniPath()
	mockedName := "foo"
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if !strings.Contains(tc.propertyFunc(mockedName), MiniPath()) {
				t.Errorf("Property %s(%v) doesn't contain miniPath %v", tc.name, tc.propertyFunc, miniPath)
			}
			if !strings.Contains(tc.propertyFunc(mockedName), mockedName) {
				t.Errorf("Property %s(%v) doesn't contain passed name %v", tc.name, tc.propertyFunc, mockedName)
			}
		})

	}
}

type propertyFnWithoutArg func() string

func TestPropertyWithoutNameArg(t *testing.T) {
	var testCases = []struct {
		propertyFunc propertyFnWithoutArg
		name         string
	}{
		{ConfigFile, "ConfigFile"},
		{CACert, "CACert"},
	}
	miniPath := MiniPath()
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if !strings.Contains(tc.propertyFunc(), MiniPath()) {
				t.Errorf("Property %s(%v) doesn't contain expected miniPath %v", tc.name, tc.propertyFunc, miniPath)
			}
		})
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 translate

import (
	"testing"

	"golang.org/x/text/language"
)

func TestSetPreferredLanguage(t *testing.T) {
	var tests = []struct {
		input string
		want  language.Tag
	}{
		{"", language.AmericanEnglish},
		{"C", language.AmericanEnglish},
		{"zh", language.Chinese},
		{"fr_FR.utf8", language.French},
	}
	for _, tc := range tests {
		t.Run(tc.input, func(t *testing.T) {
			// Set something so that we can assert change.
			if err := SetPreferredLanguage("is"); err != nil {
				t.Errorf("unexpected error: %q", err)
			}
			if err := SetPreferredLanguage(tc.input); err != nil {
				t.Errorf("unexpected error: %q", err)
			}

			want, _ := tc.want.Base()
			got, _ := GetPreferredLanguage().Base()
			if got != want {
				t.Errorf("SetPreferredLanguage(%s) = %q, want %q", tc.input, got, want)
			}
		})
	}

}
<commit_msg>add some tests to translate pkg<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 translate

import (
	"testing"

	"golang.org/x/text/language"
)

func TestSetPreferredLanguage(t *testing.T) {
	var tests = []struct {
		input string
		want  language.Tag
	}{
		{"", language.AmericanEnglish},
		{"C", language.AmericanEnglish},
		{"zh", language.Chinese},
		{"fr_FR.utf8", language.French},
	}
	for _, tc := range tests {
		t.Run(tc.input, func(t *testing.T) {
			// Set something so that we can assert change.
			if err := SetPreferredLanguage("is"); err != nil {
				t.Errorf("unexpected error: %q", err)
			}
			if err := SetPreferredLanguage(tc.input); err != nil {
				t.Errorf("unexpected error: %q", err)
			}

			want, _ := tc.want.Base()
			got, _ := GetPreferredLanguage().Base()
			if got != want {
				t.Errorf("SetPreferredLanguage(%s) = %q, want %q", tc.input, got, want)
			}
		})
	}

}

func TestT(t *testing.T) {
	var tests = []struct {
		description, input, expected string
		langDef, langPref            language.Tag
		translations                 map[string]interface{}
	}{
		{
			description: "empty string not default language",
			input:       "",
			expected:    "",
			langPref:    language.English,
			langDef:     language.Lithuanian,
		},
		{
			description: "empty string and default language",
			input:       "",
			expected:    "",
			langPref:    language.English,
			langDef:     language.English,
		},
		{
			description:  "existing translation",
			input:        "cat",
			expected:     "kot",
			langPref:     language.Lithuanian,
			langDef:      language.English,
			translations: map[string]interface{}{"cat": "kot"},
		},
		{
			description:  "not existing translation",
			input:        "cat",
			expected:     "cat",
			langPref:     language.Lithuanian,
			langDef:      language.English,
			translations: map[string]interface{}{"dog": "pies"},
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			defaultLanguage = test.langDef
			preferredLanguage = test.langPref
			Translations = test.translations
			got := T(test.input)
			if test.expected != got {
				t.Errorf("T(%v) shoud return %v, but got: %v", test.input, test.expected, got)
			}
		})
	}
}
<|endoftext|>