<commit_before>package gotest

import (
	"fmt"
	"strings"
	"time"

	"github.com/jstemmer/go-junit-report/v2/gtr"
	"github.com/jstemmer/go-junit-report/v2/parser/gotest/internal/collector"
)

const (
	globalID = 0
)

// reportBuilder helps build a test Report from a collection of events.
//
// The reportBuilder keeps track of the active context whenever a test or build
// error is created. This is necessary because the test parser do not contain
// any state themselves and simply just emit an event for every line that is
// read. By tracking the active context, any output that is appended to the
// reportBuilder gets attributed to the correct test or build error.
type reportBuilder struct {
	packages    []gtr.Package
	tests       map[int]gtr.Test
	buildErrors map[int]gtr.Error

	// state
	nextID    int               // next free unused id
	output    *collector.Output // output collected for each id
	coverage  float64           // coverage percentage
	parentIDs map[int]struct{}  // set of test id's that contain subtests

	// options
	packageName   string
	subtestMode   SubtestMode
	timestampFunc func() time.Time
}

// newReportBuilder creates a new reportBuilder.
func newReportBuilder() *reportBuilder {
	return &reportBuilder{
		tests:         make(map[int]gtr.Test),
		buildErrors:   make(map[int]gtr.Error),
		nextID:        1,
		output:        collector.New(),
		parentIDs:     make(map[int]struct{}),
		timestampFunc: time.Now,
	}
}

// ProcessEvent gives an event to this reportBuilder to be processed for this
// report.
func (b *reportBuilder) ProcessEvent(ev Event) {
	switch ev.Type {
	case "run_test":
		b.CreateTest(ev.Name)
	case "pause_test":
		b.PauseTest(ev.Name)
	case "cont_test":
		b.ContinueTest(ev.Name)
	case "end_test":
		b.EndTest(ev.Name, ev.Result, ev.Duration, ev.Indent)
	case "run_benchmark":
		b.CreateTest(ev.Name)
	case "benchmark":
		b.BenchmarkResult(ev.Name, ev.Iterations, ev.NsPerOp, ev.MBPerSec, ev.BytesPerOp, ev.AllocsPerOp)
	case "end_benchmark":
		b.EndTest(ev.Name, ev.Result, 0, 0)
	case "status":
		b.End()
	case "summary":
		b.CreatePackage(ev.Name, ev.Result, ev.Duration, ev.Data)
	case "coverage":
		b.Coverage(ev.CovPct, ev.CovPackages)
	case "build_output":
		b.CreateBuildError(ev.Name)
	case "output":
		b.AppendOutput(ev.Data)
	default:
		fmt.Printf("reportBuilder: unhandled event type: %v\n", ev.Type)
	}
}

// newID returns a new unique id and sets the active context this id.
func (b *reportBuilder) newID() int {
	id := b.nextID
	b.nextID++
	return id
}

// flush creates a new package in this report containing any tests we've
// collected so far. This is necessary when a test did not end with a summary.
func (b *reportBuilder) flush() {
	if len(b.tests) > 0 {
		b.CreatePackage(b.packageName, "", 0, "")
	}
}

// Build returns the new Report containing all the tests and output created so
// far.
func (b *reportBuilder) Build() gtr.Report {
	b.flush()
	return gtr.Report{Packages: b.packages}
}

// CreateTest adds a test with the given name to the report, and marks it as
// active.
func (b *reportBuilder) CreateTest(name string) int {
	if parentID, ok := b.findTestParentID(name); ok {
		b.parentIDs[parentID] = struct{}{}
	}
	id := b.newID()
	b.output.SetActiveID(id)
	b.tests[id] = gtr.NewTest(id, name)
	return id
}

// PauseTest marks the active context as no longer active. Any results or
// output added to the report after calling PauseTest will no longer be assumed
// to belong to this test.
func (b *reportBuilder) PauseTest(name string) {
	b.output.SetActiveID(0)
}

// ContinueTest finds the test with the given name and marks it as active. If
// more than one test exist with this name, the most recently created test will
// be used.
func (b *reportBuilder) ContinueTest(name string) {
	id, _ := b.findTest(name)
	b.output.SetActiveID(id)
}

// EndTest finds the test with the given name, sets the result, duration and
// level. If more than one test exists with this name, the most recently
// created test will be used. If no test exists with this name, a new test is
// created.
func (b *reportBuilder) EndTest(name, result string, duration time.Duration, level int) {
	id, ok := b.findTest(name)
	if !ok {
		// test did not exist, create one
		// TODO: Likely reason is that the user ran go test without the -v
		// flag, should we report this somewhere?
		id = b.CreateTest(name)
	}

	t := b.tests[id]
	t.Result = parseResult(result)
	t.Duration = duration
	t.Level = level
	b.tests[id] = t
	b.output.SetActiveID(0)
}

// End marks the active context as no longer active.
func (b *reportBuilder) End() {
	b.output.SetActiveID(0)
}

// BenchmarkResult updates an existing or adds a new test with the given
// results and marks it as active. If an existing test with this name exists
// but without result, then that one is updated. Otherwise a new one is added
// to the report.
func (b *reportBuilder) BenchmarkResult(name string, iterations int64, nsPerOp, mbPerSec float64, bytesPerOp, allocsPerOp int64) {
	id, ok := b.findTest(name)
	if !ok || b.tests[id].Result != gtr.Unknown {
		id = b.CreateTest(name)
	}

	benchmark := Benchmark{iterations, nsPerOp, mbPerSec, bytesPerOp, allocsPerOp}
	test := gtr.NewTest(id, name)
	test.Result = gtr.Pass
	test.Duration = benchmark.ApproximateDuration()
	SetBenchmarkData(&test, benchmark)
	b.tests[id] = test
}

// CreateBuildError creates a new build error and marks it as active.
func (b *reportBuilder) CreateBuildError(packageName string) {
	id := b.newID()
	b.output.SetActiveID(id)
	b.buildErrors[id] = gtr.Error{ID: id, Name: packageName}
}

// CreatePackage adds a new package with the given name to the Report. This
// package contains all the build errors, output, tests and benchmarks created
// so far. Afterwards all state is reset.
func (b *reportBuilder) CreatePackage(name, result string, duration time.Duration, data string) {
	pkg := gtr.Package{
		Name:      name,
		Duration:  duration,
		Timestamp: b.timestampFunc(),
	}

	// Build errors are treated somewhat differently. Rather than having a
	// single package with all build errors collected so far, we only care
	// about the build errors for this particular package.
	for id, buildErr := range b.buildErrors {
		if buildErr.Name == name {
			if len(b.tests) > 0 {
				panic("unexpected tests found in build error package")
			}
			buildErr.ID = id
			buildErr.Duration = duration
			buildErr.Cause = data
			buildErr.Output = b.output.Get(id)

			pkg.BuildError = buildErr
			b.packages = append(b.packages, pkg)

			delete(b.buildErrors, id)
			// TODO: reset state
			// TODO: buildErrors shouldn't reset/use nextID/lastID, they're more like a global cache
			return
		}
	}

	// If we've collected output, but there were no tests then either there
	// actually were no tests, or there was some other non-build error.
	if b.output.Contains(globalID) && len(b.tests) == 0 {
		if parseResult(result) == gtr.Fail {
			pkg.RunError = gtr.Error{
				Name:   name,
				Output: b.output.Get(globalID),
			}
		} else if b.output.Contains(globalID) {
			pkg.Output = b.output.Get(globalID)
		}
		b.packages = append(b.packages, pkg)
		b.output.Clear(globalID)
		return
	}

	// If the summary result says we failed, but there were no failing tests
	// then something else must have failed.
	if parseResult(result) == gtr.Fail && len(b.tests) > 0 && !b.containsFailures() {
		pkg.RunError = gtr.Error{
			Name:   name,
			Output: b.output.Get(globalID),
		}
		b.output.Clear(globalID)
	}

	// Collect tests for this package, maintaining insertion order.
	var tests []gtr.Test
	for id := 1; id < b.nextID; id++ {
		if t, ok := b.tests[id]; ok {
			if b.isParent(id) {
				if b.subtestMode == IgnoreParentResults {
					t.Result = gtr.Pass
				} else if b.subtestMode == ExcludeParents {
					b.output.Merge(id, globalID)
					continue
				}
			}
			t.Output = b.output.Get(id)
			tests = append(tests, t)
			continue
		}
	}
	tests = b.groupBenchmarksByName(tests)

	pkg.Coverage = b.coverage
	pkg.Output = b.output.Get(globalID)
	pkg.Tests = tests
	b.packages = append(b.packages, pkg)

	// reset state, except for nextID to ensure all id's are unique.
	b.output.SetActiveID(0)
	b.output.Clear(globalID)
	b.coverage = 0
	b.tests = make(map[int]gtr.Test)
	b.parentIDs = make(map[int]struct{})
}

// Coverage sets the code coverage percentage.
func (b *reportBuilder) Coverage(pct float64, packages []string) {
	b.coverage = pct
}

// AppendOutput appends the given text to the currently active context. If no
// active context exists, the output is assumed to belong to the package.
func (b *reportBuilder) AppendOutput(text string) {
	b.output.Append(text)
}

// findTest returns the id of the most recently created test with the given
// name if it exists.
func (b *reportBuilder) findTest(name string) (int, bool) {
	for i := b.nextID; i > 0; i-- {
		if test, ok := b.tests[i]; ok && test.Name == name {
			return i, true
		}
	}
	return 0, false
}

func (b *reportBuilder) findTestParentID(name string) (int, bool) {
	parent := dropLastSegment(name)
	for parent != "" {
		if id, ok := b.findTest(parent); ok {
			return id, true
		}
		parent = dropLastSegment(parent)
	}
	return 0, false
}

func (b *reportBuilder) isParent(id int) bool {
	_, ok := b.parentIDs[id]
	return ok
}

func dropLastSegment(name string) string {
	if idx := strings.LastIndexByte(name, '/'); idx >= 0 {
		return name[:idx]
	}
	return ""
}

// containsFailures return true if the current list of tests contains at least
// one failing test or an unknown result.
func (b *reportBuilder) containsFailures() bool {
	for _, test := range b.tests {
		if test.Result == gtr.Fail || test.Result == gtr.Unknown {
			return true
		}
	}
	return false
}

// parseResult returns a Result for the given string r.
func parseResult(r string) gtr.Result {
	switch r {
	case "PASS":
		return gtr.Pass
	case "FAIL":
		return gtr.Fail
	case "SKIP":
		return gtr.Skip
	case "BENCH":
		return gtr.Pass
	default:
		return gtr.Unknown
	}
}

func (b *reportBuilder) groupBenchmarksByName(tests []gtr.Test) []gtr.Test {
	if len(tests) == 0 {
		return nil
	}

	var grouped []gtr.Test
	byName := make(map[string][]gtr.Test)
	for _, test := range tests {
		if !strings.HasPrefix(test.Name, "Benchmark") {
			// If this test is not a benchmark, we won't group it by name but
			// just add it to the final result.
			grouped = append(grouped, test)
			continue
		}
		if _, ok := byName[test.Name]; !ok {
			grouped = append(grouped, gtr.NewTest(test.ID, test.Name))
		}
		byName[test.Name] = append(byName[test.Name], test)
	}

	for i, group := range grouped {
		if !strings.HasPrefix(group.Name, "Benchmark") {
			continue
		}
		var (
			ids   []int
			total Benchmark
			count int
		)
		for _, test := range byName[group.Name] {
			ids = append(ids, test.ID)
			if test.Result != gtr.Pass {
				continue
			}

			if bench, ok := GetBenchmarkData(test); ok {
				total.Iterations += bench.Iterations
				total.NsPerOp += bench.NsPerOp
				total.MBPerSec += bench.MBPerSec
				total.BytesPerOp += bench.BytesPerOp
				total.AllocsPerOp += bench.AllocsPerOp
				count++
			}
		}

		group.Duration = combinedDuration(byName[group.Name])
		group.Result = groupResults(byName[group.Name])
		group.Output = b.output.GetAll(ids...)
		if count > 0 {
			total.Iterations /= int64(count)
			total.NsPerOp /= float64(count)
			total.MBPerSec /= float64(count)
			total.BytesPerOp /= int64(count)
			total.AllocsPerOp /= int64(count)
			SetBenchmarkData(&group, total)
		}
		grouped[i] = group
	}
	return grouped
}

func combinedDuration(tests []gtr.Test) time.Duration {
	var total time.Duration
	for _, test := range tests {
		total += test.Duration
	}
	return total
}

func groupResults(tests []gtr.Test) gtr.Result {
	var result gtr.Result
	for _, test := range tests {
		if test.Result == gtr.Fail {
			return gtr.Fail
		}
		if result != gtr.Pass {
			result = test.Result
		}
	}
	return result
}
<commit_msg>parser/gotest: Remove unnecessary if statement when creating a package<commit_after>package gotest

import (
	"fmt"
	"strings"
	"time"

	"github.com/jstemmer/go-junit-report/v2/gtr"
	"github.com/jstemmer/go-junit-report/v2/parser/gotest/internal/collector"
)

const (
	globalID = 0
)

// reportBuilder helps build a test Report from a collection of events.
//
// The reportBuilder keeps track of the active context whenever a test or build
// error is created. This is necessary because the test parser do not contain
// any state themselves and simply just emit an event for every line that is
// read. By tracking the active context, any output that is appended to the
// reportBuilder gets attributed to the correct test or build error.
type reportBuilder struct {
	packages    []gtr.Package
	tests       map[int]gtr.Test
	buildErrors map[int]gtr.Error

	// state
	nextID    int               // next free unused id
	output    *collector.Output // output collected for each id
	coverage  float64           // coverage percentage
	parentIDs map[int]struct{}  // set of test id's that contain subtests

	// options
	packageName   string
	subtestMode   SubtestMode
	timestampFunc func() time.Time
}

// newReportBuilder creates a new reportBuilder.
func newReportBuilder() *reportBuilder {
	return &reportBuilder{
		tests:         make(map[int]gtr.Test),
		buildErrors:   make(map[int]gtr.Error),
		nextID:        1,
		output:        collector.New(),
		parentIDs:     make(map[int]struct{}),
		timestampFunc: time.Now,
	}
}

// ProcessEvent gives an event to this reportBuilder to be processed for this
// report.
func (b *reportBuilder) ProcessEvent(ev Event) {
	switch ev.Type {
	case "run_test":
		b.CreateTest(ev.Name)
	case "pause_test":
		b.PauseTest(ev.Name)
	case "cont_test":
		b.ContinueTest(ev.Name)
	case "end_test":
		b.EndTest(ev.Name, ev.Result, ev.Duration, ev.Indent)
	case "run_benchmark":
		b.CreateTest(ev.Name)
	case "benchmark":
		b.BenchmarkResult(ev.Name, ev.Iterations, ev.NsPerOp, ev.MBPerSec, ev.BytesPerOp, ev.AllocsPerOp)
	case "end_benchmark":
		b.EndTest(ev.Name, ev.Result, 0, 0)
	case "status":
		b.End()
	case "summary":
		b.CreatePackage(ev.Name, ev.Result, ev.Duration, ev.Data)
	case "coverage":
		b.Coverage(ev.CovPct, ev.CovPackages)
	case "build_output":
		b.CreateBuildError(ev.Name)
	case "output":
		b.AppendOutput(ev.Data)
	default:
		fmt.Printf("reportBuilder: unhandled event type: %v\n", ev.Type)
	}
}

// newID returns a new unique id and sets the active context this id.
func (b *reportBuilder) newID() int {
	id := b.nextID
	b.nextID++
	return id
}

// flush creates a new package in this report containing any tests we've
// collected so far. This is necessary when a test did not end with a summary.
func (b *reportBuilder) flush() {
	if len(b.tests) > 0 {
		b.CreatePackage(b.packageName, "", 0, "")
	}
}

// Build returns the new Report containing all the tests and output created so
// far.
func (b *reportBuilder) Build() gtr.Report {
	b.flush()
	return gtr.Report{Packages: b.packages}
}

// CreateTest adds a test with the given name to the report, and marks it as
// active.
func (b *reportBuilder) CreateTest(name string) int {
	if parentID, ok := b.findTestParentID(name); ok {
		b.parentIDs[parentID] = struct{}{}
	}
	id := b.newID()
	b.output.SetActiveID(id)
	b.tests[id] = gtr.NewTest(id, name)
	return id
}

// PauseTest marks the active context as no longer active. Any results or
// output added to the report after calling PauseTest will no longer be assumed
// to belong to this test.
func (b *reportBuilder) PauseTest(name string) {
	b.output.SetActiveID(0)
}

// ContinueTest finds the test with the given name and marks it as active. If
// more than one test exist with this name, the most recently created test will
// be used.
func (b *reportBuilder) ContinueTest(name string) {
	id, _ := b.findTest(name)
	b.output.SetActiveID(id)
}

// EndTest finds the test with the given name, sets the result, duration and
// level. If more than one test exists with this name, the most recently
// created test will be used. If no test exists with this name, a new test is
// created.
func (b *reportBuilder) EndTest(name, result string, duration time.Duration, level int) {
	id, ok := b.findTest(name)
	if !ok {
		// test did not exist, create one
		// TODO: Likely reason is that the user ran go test without the -v
		// flag, should we report this somewhere?
		id = b.CreateTest(name)
	}

	t := b.tests[id]
	t.Result = parseResult(result)
	t.Duration = duration
	t.Level = level
	b.tests[id] = t
	b.output.SetActiveID(0)
}

// End marks the active context as no longer active.
func (b *reportBuilder) End() {
	b.output.SetActiveID(0)
}

// BenchmarkResult updates an existing or adds a new test with the given
// results and marks it as active. If an existing test with this name exists
// but without result, then that one is updated. Otherwise a new one is added
// to the report.
func (b *reportBuilder) BenchmarkResult(name string, iterations int64, nsPerOp, mbPerSec float64, bytesPerOp, allocsPerOp int64) {
	id, ok := b.findTest(name)
	if !ok || b.tests[id].Result != gtr.Unknown {
		id = b.CreateTest(name)
	}

	benchmark := Benchmark{iterations, nsPerOp, mbPerSec, bytesPerOp, allocsPerOp}
	test := gtr.NewTest(id, name)
	test.Result = gtr.Pass
	test.Duration = benchmark.ApproximateDuration()
	SetBenchmarkData(&test, benchmark)
	b.tests[id] = test
}

// CreateBuildError creates a new build error and marks it as active.
func (b *reportBuilder) CreateBuildError(packageName string) {
	id := b.newID()
	b.output.SetActiveID(id)
	b.buildErrors[id] = gtr.Error{ID: id, Name: packageName}
}

// CreatePackage adds a new package with the given name to the Report. This
// package contains all the build errors, output, tests and benchmarks created
// so far. Afterwards all state is reset.
func (b *reportBuilder) CreatePackage(name, result string, duration time.Duration, data string) {
	pkg := gtr.Package{
		Name:      name,
		Duration:  duration,
		Timestamp: b.timestampFunc(),
	}

	// Build errors are treated somewhat differently. Rather than having a
	// single package with all build errors collected so far, we only care
	// about the build errors for this particular package.
	for id, buildErr := range b.buildErrors {
		if buildErr.Name == name {
			if len(b.tests) > 0 {
				panic("unexpected tests found in build error package")
			}
			buildErr.ID = id
			buildErr.Duration = duration
			buildErr.Cause = data
			buildErr.Output = b.output.Get(id)

			pkg.BuildError = buildErr
			b.packages = append(b.packages, pkg)

			delete(b.buildErrors, id)
			// TODO: reset state
			// TODO: buildErrors shouldn't reset/use nextID/lastID, they're more like a global cache
			return
		}
	}

	// If we've collected output, but there were no tests then either there
	// actually were no tests, or there was some other non-build error.
	if b.output.Contains(globalID) && len(b.tests) == 0 {
		if parseResult(result) == gtr.Fail {
			pkg.RunError = gtr.Error{
				Name:   name,
				Output: b.output.Get(globalID),
			}
		} else {
			pkg.Output = b.output.Get(globalID)
		}
		b.packages = append(b.packages, pkg)
		b.output.Clear(globalID)
		return
	}

	// If the summary result says we failed, but there were no failing tests
	// then something else must have failed.
	if parseResult(result) == gtr.Fail && len(b.tests) > 0 && !b.containsFailures() {
		pkg.RunError = gtr.Error{
			Name:   name,
			Output: b.output.Get(globalID),
		}
		b.output.Clear(globalID)
	}

	// Collect tests for this package, maintaining insertion order.
	var tests []gtr.Test
	for id := 1; id < b.nextID; id++ {
		if t, ok := b.tests[id]; ok {
			if b.isParent(id) {
				if b.subtestMode == IgnoreParentResults {
					t.Result = gtr.Pass
				} else if b.subtestMode == ExcludeParents {
					b.output.Merge(id, globalID)
					continue
				}
			}
			t.Output = b.output.Get(id)
			tests = append(tests, t)
			continue
		}
	}
	tests = b.groupBenchmarksByName(tests)

	pkg.Coverage = b.coverage
	pkg.Output = b.output.Get(globalID)
	pkg.Tests = tests
	b.packages = append(b.packages, pkg)

	// reset state, except for nextID to ensure all id's are unique.
	b.output.SetActiveID(0)
	b.output.Clear(globalID)
	b.coverage = 0
	b.tests = make(map[int]gtr.Test)
	b.parentIDs = make(map[int]struct{})
}

// Coverage sets the code coverage percentage.
func (b *reportBuilder) Coverage(pct float64, packages []string) {
	b.coverage = pct
}

// AppendOutput appends the given text to the currently active context. If no
// active context exists, the output is assumed to belong to the package.
func (b *reportBuilder) AppendOutput(text string) {
	b.output.Append(text)
}

// findTest returns the id of the most recently created test with the given
// name if it exists.
func (b *reportBuilder) findTest(name string) (int, bool) {
	for i := b.nextID; i > 0; i-- {
		if test, ok := b.tests[i]; ok && test.Name == name {
			return i, true
		}
	}
	return 0, false
}

func (b *reportBuilder) findTestParentID(name string) (int, bool) {
	parent := dropLastSegment(name)
	for parent != "" {
		if id, ok := b.findTest(parent); ok {
			return id, true
		}
		parent = dropLastSegment(parent)
	}
	return 0, false
}

func (b *reportBuilder) isParent(id int) bool {
	_, ok := b.parentIDs[id]
	return ok
}

func dropLastSegment(name string) string {
	if idx := strings.LastIndexByte(name, '/'); idx >= 0 {
		return name[:idx]
	}
	return ""
}

// containsFailures return true if the current list of tests contains at least
// one failing test or an unknown result.
func (b *reportBuilder) containsFailures() bool {
	for _, test := range b.tests {
		if test.Result == gtr.Fail || test.Result == gtr.Unknown {
			return true
		}
	}
	return false
}

// parseResult returns a Result for the given string r.
func parseResult(r string) gtr.Result {
	switch r {
	case "PASS":
		return gtr.Pass
	case "FAIL":
		return gtr.Fail
	case "SKIP":
		return gtr.Skip
	case "BENCH":
		return gtr.Pass
	default:
		return gtr.Unknown
	}
}

func (b *reportBuilder) groupBenchmarksByName(tests []gtr.Test) []gtr.Test {
	if len(tests) == 0 {
		return nil
	}

	var grouped []gtr.Test
	byName := make(map[string][]gtr.Test)
	for _, test := range tests {
		if !strings.HasPrefix(test.Name, "Benchmark") {
			// If this test is not a benchmark, we won't group it by name but
			// just add it to the final result.
			grouped = append(grouped, test)
			continue
		}
		if _, ok := byName[test.Name]; !ok {
			grouped = append(grouped, gtr.NewTest(test.ID, test.Name))
		}
		byName[test.Name] = append(byName[test.Name], test)
	}

	for i, group := range grouped {
		if !strings.HasPrefix(group.Name, "Benchmark") {
			continue
		}
		var (
			ids   []int
			total Benchmark
			count int
		)
		for _, test := range byName[group.Name] {
			ids = append(ids, test.ID)
			if test.Result != gtr.Pass {
				continue
			}

			if bench, ok := GetBenchmarkData(test); ok {
				total.Iterations += bench.Iterations
				total.NsPerOp += bench.NsPerOp
				total.MBPerSec += bench.MBPerSec
				total.BytesPerOp += bench.BytesPerOp
				total.AllocsPerOp += bench.AllocsPerOp
				count++
			}
		}

		group.Duration = combinedDuration(byName[group.Name])
		group.Result = groupResults(byName[group.Name])
		group.Output = b.output.GetAll(ids...)
		if count > 0 {
			total.Iterations /= int64(count)
			total.NsPerOp /= float64(count)
			total.MBPerSec /= float64(count)
			total.BytesPerOp /= int64(count)
			total.AllocsPerOp /= int64(count)
			SetBenchmarkData(&group, total)
		}
		grouped[i] = group
	}
	return grouped
}

func combinedDuration(tests []gtr.Test) time.Duration {
	var total time.Duration
	for _, test := range tests {
		total += test.Duration
	}
	return total
}

func groupResults(tests []gtr.Test) gtr.Result {
	var result gtr.Result
	for _, test := range tests {
		if test.Result == gtr.Fail {
			return gtr.Fail
		}
		if result != gtr.Pass {
			result = test.Result
		}
	}
	return result
}
<|endoftext|>


<commit_before>package sudoku

import (
	"encoding/csv"
	"log"
	"os"
	"strconv"
)

/*
	This file is where the basic solve technique infrastructure is defined.

	The techniques that Human Solve uses are initalized and stored into human_solves.go Techniques slice here.

	Specific techniques are implemented in hst_*.go files (hst == human solve techniques), where there's
	a separate file for each class of technique.

*/

//You shouldn't just create a technique by hand (the interior basicSolveTechnique needs to be initalized with the right values)
//So in the rare cases where you want to grab a technique by name, grab it from here.
//TODO: it feels like a pattern smell that there's only a singleton for each technique that you can't cons up on demand.
var techniquesByName map[string]SolveTechnique

type SolveTechnique interface {
	Name() string
	Description(*SolveStep) string
	//IMPORTANT: a step should return a step IFF that step is valid AND the step would cause useful work to be done if applied.

	//Find returns as many steps as it can find in the grid for that technique. This helps ensure that when we pick a step,
	//it's more likely to be an "easy" step because there will be more of them at any time.
	Find(*Grid) []*SolveStep
	IsFill() bool
	//How difficult a real human would say this technique is. Generally inversely related to how often a real person would pick it. 0.0 to 1.0.
	Difficulty() float64
	//Not exported; used for us to late-bind weights from a file.
	setDifficulty(difficulty float64)
}

type cellGroupType int

const (
	GROUP_NONE = iota
	GROUP_ROW
	GROUP_COL
	GROUP_BLOCK
)

type basicSolveTechnique struct {
	name      string
	isFill    bool
	groupType cellGroupType
	//Size of set in technique, e.g. single = 1, pair = 2, triple = 3
	//Used for generating descriptions in some sub-structs.
	k          int
	difficulty float64
}

func init() {

	//TODO: calculate more realistic weights.

	CheapTechniques = []SolveTechnique{
		hiddenSingleTechnique{
			basicSolveTechnique{
				//TODO: shouldn't this be "Hidden Single Row" (and likewise for others)
				"Necessary In Row",
				true,
				GROUP_ROW,
				1,
				0.0,
			},
		},
		hiddenSingleTechnique{
			basicSolveTechnique{
				"Necessary In Col",
				true,
				GROUP_COL,
				1,
				0.0,
			},
		},
		hiddenSingleTechnique{
			basicSolveTechnique{
				"Necessary In Block",
				true,
				GROUP_BLOCK,
				1,
				0.0,
			},
		},
		nakedSingleTechnique{
			basicSolveTechnique{
				//TODO: shouldn't this name be Naked Single for consistency?
				"Only Legal Number",
				true,
				GROUP_NONE,
				1,
				5.0,
			},
		},
		pointingPairTechnique{
			basicSolveTechnique{
				"Pointing Pair Row",
				false,
				GROUP_ROW,
				2,
				25.0,
			},
		},
		pointingPairTechnique{
			basicSolveTechnique{
				"Pointing Pair Col",
				false,
				GROUP_COL,
				2,
				25.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Pair Col",
				false,
				GROUP_COL,
				2,
				75.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Pair Row",
				false,
				GROUP_ROW,
				2,
				75.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Pair Block",
				false,
				GROUP_BLOCK,
				2,
				85.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Triple Col",
				false,
				GROUP_COL,
				3,
				125.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Triple Row",
				false,
				GROUP_ROW,
				3,
				125.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Triple Block",
				false,
				GROUP_BLOCK,
				3,
				140.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Quad Col",
				false,
				GROUP_COL,
				4,
				250.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Quad Row",
				false,
				GROUP_ROW,
				4,
				250.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Quad Block",
				false,
				GROUP_BLOCK,
				4,
				280.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Pair Row",
				false,
				GROUP_ROW,
				2,
				300.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Pair Col",
				false,
				GROUP_COL,
				2,
				300.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Pair Block",
				false,
				GROUP_BLOCK,
				2,
				250.0,
			},
		},
	}

	ExpensiveTechniques = []SolveTechnique{
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Triple Row",
				false,
				GROUP_ROW,
				3,
				600.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Triple Col",
				false,
				GROUP_COL,
				3,
				600.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Triple Block",
				false,
				GROUP_BLOCK,
				3,
				500.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Quad Row",
				false,
				GROUP_ROW,
				4,
				1200.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Quad Col",
				false,
				GROUP_COL,
				4,
				1200.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Quad Block",
				false,
				GROUP_BLOCK,
				4,
				1000.0,
			},
		},
	}

	Techniques = append(CheapTechniques, ExpensiveTechniques...)

	techniquesByName = make(map[string]SolveTechnique)

	for _, technique := range Techniques {
		techniquesByName[technique.Name()] = technique
	}

	//TODO: burn in a good set of difficulties, and don't load this file by default.
	loadDifficulties("difficulties.csv")

}

func loadDifficulties(fileName string) {

	//TODO: test that this loading works.

	log.Println("Attempting to configure difficulties from ", fileName)

	inputFile, err := os.Open(fileName)
	if err != nil {

		log.Fatal("Could not open the specified input CSV.")

	}
	defer inputFile.Close()
	csvIn := csv.NewReader(inputFile)
	records, csvErr := csvIn.ReadAll()
	if csvErr != nil {
		log.Fatal("The provided CSV could not be parsed.")
	}

	//Load up the weights into a map.
	techniqueDifficulties := make(map[string]float64)
	for i, record := range records {
		if len(record) != 2 {
			log.Fatalln("Record in weights csv wasn't right size: ", i)
		}
		theFloat, err := strconv.ParseFloat(record[1], 64)
		if err != nil {
			log.Fatalln("Record in weights had an invalid float: ", i)
		}
		techniqueDifficulties[record[0]] = theFloat
	}

	validNames := 0
	//TODO: report on which names were invalid.
	for name, val := range techniqueDifficulties {
		technique, ok := techniquesByName[name]
		if ok {
			technique.setDifficulty(val)
			validNames++
		} else {
			//TODO: handle "Constant"

			log.Println("Couldn't find technique provided in weights CSV: ", name)

		}
	}
	if validNames != len(techniqueDifficulties) {
		log.Println(len(techniqueDifficulties)-validNames, "difficulties were in CSV but did not align with weights.")
	}
}

func (self basicSolveTechnique) setDifficulty(difficulty float64) {
	self.difficulty = difficulty
}

func (self basicSolveTechnique) Name() string {
	return self.name
}

func (self basicSolveTechnique) IsFill() bool {
	return self.isFill
}

func (self basicSolveTechnique) Difficulty() float64 {
	return self.difficulty
}

func (self basicSolveTechnique) getter(grid *Grid) func(int) CellList {
	switch self.groupType {
	case GROUP_ROW:
		return func(i int) CellList {
			return grid.Row(i)
		}
	case GROUP_COL:
		return func(i int) CellList {
			return grid.Col(i)
		}
	case GROUP_BLOCK:
		return func(i int) CellList {
			return grid.Block(i)
		}
	default:
		//This should never happen in normal execution--the rare techniques where it doesn't work should never call getter.
		log.Println("Asked for a getter for a function with GROUP_NONE")
		//Return a shell of  a function just to not trip up things downstream.
		return func(i int) CellList {
			return nil
		}
	}
}

//This is useful both for hidden and naked subset techniques
func subsetIndexes(len int, size int) [][]int {
	//Given size of array to generate subset for, and size of desired subset, returns an array of all subset-indexes to try.
	//Sanity check
	if size > len {
		return nil
	}

	//returns an array of slices of size size that give you all of the subsets of a list of length len
	result := make([][]int, 0)
	counters := make([]int, size)
	for i, _ := range counters {
		counters[i] = i
	}
	for {
		innerResult := make([]int, size)
		for i, counter := range counters {
			innerResult[i] = counter
		}
		result = append(result, innerResult)
		//Now, increment.
		//Start at the end and try to increment each counter one.
		incremented := false
		for i := size - 1; i >= 0; i-- {

			counter := counters[i]
			if counter < len-(size-i) {
				//Found one!
				counters[i]++
				incremented = true
				if i < size-1 {
					//It was an inner counter; need to set all of the higher counters to one above the one to the left.
					base := counters[i] + 1
					for j := i + 1; j < size; j++ {
						counters[j] = base
						base++
					}
				}
				break
			}
		}
		//If we couldn't increment any, there's nothing to do.
		if !incremented {
			break
		}
	}
	return result
}
<commit_msg>Made loadDifficulties return a bool if successful<commit_after>package sudoku

import (
	"encoding/csv"
	"log"
	"os"
	"strconv"
)

/*
	This file is where the basic solve technique infrastructure is defined.

	The techniques that Human Solve uses are initalized and stored into human_solves.go Techniques slice here.

	Specific techniques are implemented in hst_*.go files (hst == human solve techniques), where there's
	a separate file for each class of technique.

*/

//You shouldn't just create a technique by hand (the interior basicSolveTechnique needs to be initalized with the right values)
//So in the rare cases where you want to grab a technique by name, grab it from here.
//TODO: it feels like a pattern smell that there's only a singleton for each technique that you can't cons up on demand.
var techniquesByName map[string]SolveTechnique

type SolveTechnique interface {
	Name() string
	Description(*SolveStep) string
	//IMPORTANT: a step should return a step IFF that step is valid AND the step would cause useful work to be done if applied.

	//Find returns as many steps as it can find in the grid for that technique. This helps ensure that when we pick a step,
	//it's more likely to be an "easy" step because there will be more of them at any time.
	Find(*Grid) []*SolveStep
	IsFill() bool
	//How difficult a real human would say this technique is. Generally inversely related to how often a real person would pick it. 0.0 to 1.0.
	Difficulty() float64
	//Not exported; used for us to late-bind weights from a file.
	setDifficulty(difficulty float64)
}

type cellGroupType int

const (
	GROUP_NONE = iota
	GROUP_ROW
	GROUP_COL
	GROUP_BLOCK
)

type basicSolveTechnique struct {
	name      string
	isFill    bool
	groupType cellGroupType
	//Size of set in technique, e.g. single = 1, pair = 2, triple = 3
	//Used for generating descriptions in some sub-structs.
	k          int
	difficulty float64
}

func init() {

	//TODO: calculate more realistic weights.

	CheapTechniques = []SolveTechnique{
		hiddenSingleTechnique{
			basicSolveTechnique{
				//TODO: shouldn't this be "Hidden Single Row" (and likewise for others)
				"Necessary In Row",
				true,
				GROUP_ROW,
				1,
				0.0,
			},
		},
		hiddenSingleTechnique{
			basicSolveTechnique{
				"Necessary In Col",
				true,
				GROUP_COL,
				1,
				0.0,
			},
		},
		hiddenSingleTechnique{
			basicSolveTechnique{
				"Necessary In Block",
				true,
				GROUP_BLOCK,
				1,
				0.0,
			},
		},
		nakedSingleTechnique{
			basicSolveTechnique{
				//TODO: shouldn't this name be Naked Single for consistency?
				"Only Legal Number",
				true,
				GROUP_NONE,
				1,
				5.0,
			},
		},
		pointingPairTechnique{
			basicSolveTechnique{
				"Pointing Pair Row",
				false,
				GROUP_ROW,
				2,
				25.0,
			},
		},
		pointingPairTechnique{
			basicSolveTechnique{
				"Pointing Pair Col",
				false,
				GROUP_COL,
				2,
				25.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Pair Col",
				false,
				GROUP_COL,
				2,
				75.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Pair Row",
				false,
				GROUP_ROW,
				2,
				75.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Pair Block",
				false,
				GROUP_BLOCK,
				2,
				85.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Triple Col",
				false,
				GROUP_COL,
				3,
				125.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Triple Row",
				false,
				GROUP_ROW,
				3,
				125.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Triple Block",
				false,
				GROUP_BLOCK,
				3,
				140.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Quad Col",
				false,
				GROUP_COL,
				4,
				250.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Quad Row",
				false,
				GROUP_ROW,
				4,
				250.0,
			},
		},
		nakedSubsetTechnique{
			basicSolveTechnique{
				"Naked Quad Block",
				false,
				GROUP_BLOCK,
				4,
				280.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Pair Row",
				false,
				GROUP_ROW,
				2,
				300.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Pair Col",
				false,
				GROUP_COL,
				2,
				300.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Pair Block",
				false,
				GROUP_BLOCK,
				2,
				250.0,
			},
		},
	}

	ExpensiveTechniques = []SolveTechnique{
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Triple Row",
				false,
				GROUP_ROW,
				3,
				600.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Triple Col",
				false,
				GROUP_COL,
				3,
				600.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Triple Block",
				false,
				GROUP_BLOCK,
				3,
				500.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Quad Row",
				false,
				GROUP_ROW,
				4,
				1200.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Quad Col",
				false,
				GROUP_COL,
				4,
				1200.0,
			},
		},
		hiddenSubsetTechnique{
			basicSolveTechnique{
				"Hidden Quad Block",
				false,
				GROUP_BLOCK,
				4,
				1000.0,
			},
		},
	}

	Techniques = append(CheapTechniques, ExpensiveTechniques...)

	techniquesByName = make(map[string]SolveTechnique)

	for _, technique := range Techniques {
		techniquesByName[technique.Name()] = technique
	}

	//TODO: burn in a good set of difficulties, and don't load this file by default.
	loadDifficulties("difficulties.csv")

}

func loadDifficulties(fileName string) bool {

	//TODO: test that this loading works.

	log.Println("Attempting to configure difficulties from ", fileName)

	inputFile, err := os.Open(fileName)
	if err != nil {

		log.Println("Could not open the specified input CSV.")
		return false

	}
	defer inputFile.Close()
	csvIn := csv.NewReader(inputFile)
	records, csvErr := csvIn.ReadAll()
	if csvErr != nil {
		log.Println("The provided CSV could not be parsed.")
		return false
	}

	//Load up the weights into a map.
	techniqueDifficulties := make(map[string]float64)
	for i, record := range records {
		if len(record) != 2 {
			log.Fatalln("Record in weights csv wasn't right size: ", i)
		}
		theFloat, err := strconv.ParseFloat(record[1], 64)
		if err != nil {
			log.Fatalln("Record in weights had an invalid float: ", i)
		}
		techniqueDifficulties[record[0]] = theFloat
	}

	validNames := 0
	//TODO: report on which names were invalid.
	for name, val := range techniqueDifficulties {
		technique, ok := techniquesByName[name]
		if ok {
			technique.setDifficulty(val)
			validNames++
		} else {
			//TODO: handle "Constant"

			log.Println("Couldn't find technique provided in weights CSV: ", name)

		}
	}
	if validNames != len(techniqueDifficulties) {
		log.Println(len(techniqueDifficulties)-validNames, "difficulties were in CSV but did not align with weights.")
	}
	return true
}

func (self basicSolveTechnique) setDifficulty(difficulty float64) {
	self.difficulty = difficulty
}

func (self basicSolveTechnique) Name() string {
	return self.name
}

func (self basicSolveTechnique) IsFill() bool {
	return self.isFill
}

func (self basicSolveTechnique) Difficulty() float64 {
	return self.difficulty
}

func (self basicSolveTechnique) getter(grid *Grid) func(int) CellList {
	switch self.groupType {
	case GROUP_ROW:
		return func(i int) CellList {
			return grid.Row(i)
		}
	case GROUP_COL:
		return func(i int) CellList {
			return grid.Col(i)
		}
	case GROUP_BLOCK:
		return func(i int) CellList {
			return grid.Block(i)
		}
	default:
		//This should never happen in normal execution--the rare techniques where it doesn't work should never call getter.
		log.Println("Asked for a getter for a function with GROUP_NONE")
		//Return a shell of  a function just to not trip up things downstream.
		return func(i int) CellList {
			return nil
		}
	}
}

//This is useful both for hidden and naked subset techniques
func subsetIndexes(len int, size int) [][]int {
	//Given size of array to generate subset for, and size of desired subset, returns an array of all subset-indexes to try.
	//Sanity check
	if size > len {
		return nil
	}

	//returns an array of slices of size size that give you all of the subsets of a list of length len
	result := make([][]int, 0)
	counters := make([]int, size)
	for i, _ := range counters {
		counters[i] = i
	}
	for {
		innerResult := make([]int, size)
		for i, counter := range counters {
			innerResult[i] = counter
		}
		result = append(result, innerResult)
		//Now, increment.
		//Start at the end and try to increment each counter one.
		incremented := false
		for i := size - 1; i >= 0; i-- {

			counter := counters[i]
			if counter < len-(size-i) {
				//Found one!
				counters[i]++
				incremented = true
				if i < size-1 {
					//It was an inner counter; need to set all of the higher counters to one above the one to the left.
					base := counters[i] + 1
					for j := i + 1; j < size; j++ {
						counters[j] = base
						base++
					}
				}
				break
			}
		}
		//If we couldn't increment any, there's nothing to do.
		if !incremented {
			break
		}
	}
	return result
}
<|endoftext|>


<commit_before>package freetree

import (
	"runtime"
	"runtime/debug"
	"sort"
)

// -----------------------------------------------------------------------------

// SimpleTree implements a simple binary search tree.
type SimpleTree struct {
	root  *simpleNode
	nodes uint
}

// NewSimpleTree returns an empty SimpleTree.
func NewSimpleTree() *SimpleTree {
	return &SimpleTree{}
}

// Insert inserts the given Comparables in the tree.
// It does not rebalance the tree, use Rebalance() for that.
//
// If the tree is currently empty and the passed-in Comparable are already
// sorted in increasing order, the tree will be perfectly balanced.
// This means you don't have to Rebalance() the tree if you've inserted all
// your pre-sorted data in one Insert() call.
func (st *SimpleTree) Insert(cs ...Comparable) *SimpleTree {
	return st.InsertArray(cs)
}

// InsertArray is a helper to use Insert() with a ComparableArray.
func (st *SimpleTree) InsertArray(ca ComparableArray) *SimpleTree {
	st.insert(ca, st.nodes)
	st.nodes += uint(len(ca))

	return st
}

func (st *SimpleTree) insert(ca ComparableArray, id uint) {
	l := len(ca)
	if l == 0 {
		return
	}

	st.root = st.root.insert(ca[l/2], id)

	if l > 1 {
		st.insert(ca[:l/2], id)
		st.insert(ca[l/2+1:], id)
	}
}

// Ascend returns the first element in the tree that is == `pivot`.
func (st SimpleTree) Ascend(pivot Comparable) Comparable {
	return st.ascend(pivot)
}

func (st SimpleTree) ascend(pivot Comparable) Comparable {
	return st.root.ascend(pivot)
}

// Rebalance rebalances the tree to guarantee O(log(n)) search complexity.
//
// Rebalancing is implemented as straightforwardly as possible: it's dumb.
// I strongly suggest running the garbage collector and scavenger once it's done.
//   runtime.GC()
//   debug.FreeOSMemory()
func (st *SimpleTree) Rebalance() *SimpleTree {
	flat := st.flatten()
	sort.Sort(flat)

	st.root = nil
	st.nodes = 0
	st.insert(flat, 0)

	return st
}

// Rebalance rebalances the tree and runs the garbage collector.
func (st *SimpleTree) RebalanceGC() *SimpleTree {
	st.Rebalance()
	runtime.GC()
	debug.FreeOSMemory()

	return st
}

// Flatten returns the content of the tree as a ComparableArray.
func (st SimpleTree) Flatten() ComparableArray {
	return st.flatten()
}

func (st SimpleTree) flatten() ComparableArray {
	ca := make(ComparableArray, 0, st.nodes)
	return st.root.flatten(ca)
}

// -----------------------------------------------------------------------------

type simpleNode struct {
	id          uint
	left, right *simpleNode
	data        Comparable
}

func (sn *simpleNode) insert(c Comparable, id uint) *simpleNode {
	if sn == nil {
		return &simpleNode{id: id, data: c}
	}

	if c.Less(sn.data) {
		sn.left = sn.left.insert(c, id)
	} else {
		sn.right = sn.right.insert(c, id)
	}

	return sn
}

func (sn *simpleNode) ascend(pivot Comparable) Comparable {
	if sn == nil {
		return nil
	}

	if pivot.Less(sn.data) {
		return sn.left.ascend(pivot)
	} else if sn.data.Less(pivot) {
		return sn.right.ascend(pivot)
	}

	return sn.data
}

func (sn *simpleNode) flatten(ca ComparableArray) ComparableArray {
	if sn == nil {
		return ca
	}

	ca = sn.left.flatten(ca)
	ca = sn.right.flatten(ca)

	return append(ca, sn.data)
}
<commit_msg>SimpleTree - implemented flattenNodes()<commit_after>package freetree

import (
	"runtime"
	"runtime/debug"
	"sort"
)

// -----------------------------------------------------------------------------

// SimpleTree implements a simple binary search tree.
type SimpleTree struct {
	root  *simpleNode
	nodes uint
}

// NewSimpleTree returns an empty SimpleTree.
func NewSimpleTree() *SimpleTree {
	return &SimpleTree{}
}

// Insert inserts the given Comparables in the tree.
// It does not rebalance the tree, use Rebalance() for that.
//
// If the tree is currently empty and the passed-in Comparable are already
// sorted in increasing order, the tree will be perfectly balanced.
// This means you don't have to Rebalance() the tree if you've inserted all
// your pre-sorted data in one Insert() call.
func (st *SimpleTree) Insert(cs ...Comparable) *SimpleTree {
	return st.InsertArray(cs)
}

// InsertArray is a helper to use Insert() with a ComparableArray.
func (st *SimpleTree) InsertArray(ca ComparableArray) *SimpleTree {
	st.insert(ca, st.nodes)
	st.nodes += uint(len(ca))

	return st
}

func (st *SimpleTree) insert(ca ComparableArray, id uint) {
	l := len(ca)
	if l == 0 {
		return
	}

	st.root = st.root.insert(ca[l/2], id)

	if l > 1 {
		st.insert(ca[:l/2], id)
		st.insert(ca[l/2+1:], id)
	}
}

// Ascend returns the first element in the tree that is == `pivot`.
func (st SimpleTree) Ascend(pivot Comparable) Comparable {
	return st.ascend(pivot)
}

func (st SimpleTree) ascend(pivot Comparable) Comparable {
	return st.root.ascend(pivot)
}

// Rebalance rebalances the tree to guarantee O(log(n)) search complexity.
//
// Rebalancing is implemented as straightforwardly as possible: it's dumb.
// I strongly suggest running the garbage collector and scavenger once it's done.
//   runtime.GC()
//   debug.FreeOSMemory()
func (st *SimpleTree) Rebalance() *SimpleTree {
	flat := st.flatten()
	sort.Sort(flat)

	st.root = nil
	st.nodes = 0
	st.insert(flat, 0)

	return st
}

// Rebalance rebalances the tree and runs the garbage collector.
func (st *SimpleTree) RebalanceGC() *SimpleTree {
	st.Rebalance()
	runtime.GC()
	debug.FreeOSMemory()

	return st
}

// Flatten returns the content of the tree as a ComparableArray.
func (st SimpleTree) Flatten() ComparableArray {
	return st.flatten()
}

func (st SimpleTree) flatten() ComparableArray {
	ca := make(ComparableArray, 0, st.nodes)
	return st.root.flatten(ca)
}

func (st SimpleTree) flattenNodes() []*simpleNode {
	na := make([]*simpleNode, 0, st.nodes)
	return st.root.flattenNodes(na)
}

// -----------------------------------------------------------------------------

type simpleNode struct {
	id          uint
	left, right *simpleNode
	data        Comparable
}

func (sn *simpleNode) insert(c Comparable, id uint) *simpleNode {
	if sn == nil {
		return &simpleNode{id: id, data: c}
	}

	if c.Less(sn.data) {
		sn.left = sn.left.insert(c, id)
	} else {
		sn.right = sn.right.insert(c, id)
	}

	return sn
}

func (sn *simpleNode) ascend(pivot Comparable) Comparable {
	if sn == nil {
		return nil
	}

	if pivot.Less(sn.data) {
		return sn.left.ascend(pivot)
	} else if sn.data.Less(pivot) {
		return sn.right.ascend(pivot)
	}

	return sn.data
}

func (sn *simpleNode) flatten(ca ComparableArray) ComparableArray {
	if sn == nil {
		return ca
	}

	ca = sn.left.flatten(ca)
	ca = sn.right.flatten(ca)

	return append(ca, sn.data)
}

func (sn *simpleNode) flattenNodes(na []*simpleNode) []*simpleNode {
	if sn == nil {
		return na
	}

	na = sn.left.flattenNodes(na)
	na = sn.right.flattenNodes(na)

	return append(na, sn)
}
<|endoftext|>


<commit_before>package html // import "github.com/tdewolff/parse/html"

import (
	"bytes"
	"fmt"
	"io"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

func helperStringify(t *testing.T, input string) string {
	s := ""
	l := NewLexer(bytes.NewBufferString(input))
	for i := 0; i < 10; i++ {
		tt, data := l.Next()
		if tt == ErrorToken {
			s += tt.String() + "('" + l.Err().Error() + "')"
			break
		} else if tt == AttributeToken {
			s += tt.String() + "('" + string(data) + "=" + string(l.AttrVal()) + "') "
		} else {
			s += tt.String() + "('" + string(data) + "') "
		}
	}
	return s
}

////////////////////////////////////////////////////////////////

type TTs []TokenType

func TestTokens(t *testing.T) {
	var tokenTests = []struct {
		html     string
		expected TTs
	}{
		{"", TTs{StartTagToken, StartTagCloseToken, EndTagToken}},
		{"", TTs{StartTagToken, StartTagVoidToken}},
		{"", TTs{CommentToken}},
		{"</script>", TTs{StartTagToken, StartTagCloseToken, TextToken, EndTagToken, TextToken, EndTagToken}},
		{"';--></script>", TTs{StartTagToken, StartTagCloseToken, TextToken, EndTagToken}},
		{"", TTs{StartTagToken, StartTagCloseToken, TextToken, EndTagToken}},
		{"<![CDATA[ test ]]>", TTs{TextToken}},

		// early endings
		{"", TTs{StartTagToken, StartTagCloseToken, TextToken}},

		// go-fuzz
		{"</>", TTs{EndTagToken}},
	}
	for _, test := range tokenTests {
		expected := []TokenType(test.expected)
		stringify := helperStringify(t, test.html)
		l := NewLexer(bytes.NewBufferString(test.html))
		i := 0
		for {
			tt, _ := l.Next()
			if tt == ErrorToken {
				assert.Equal(t, io.EOF, l.Err(), "error must be EOF in "+stringify)
				assert.Equal(t, len(expected), i, "when error occurred we must be at the end in "+stringify)
				break
			}
			assert.False(t, i >= len(expected), "index must not exceed expected token types size in "+stringify)
			if i < len(expected) {
				assert.Equal(t, expected[i], tt, "token types must match at index "+strconv.Itoa(i)+" in "+stringify)
			}
			i++
		}
	}

	assert.Equal(t, "Invalid(100)", TokenType(100).String())
}

func TestTags(t *testing.T) {
	var tagTests = []struct {
		html     string
		expected string
	}{
		{"<foo:bar.qux-norf/>", "foo:bar.qux-norf"},
		{"<foo?bar/qux>", "foo?bar/qux"},
		{"<!DOCTYPE note SYSTEM \"Note.dtd\">", " note SYSTEM \"Note.dtd\""},

		// early endings
		{"<foo ", "foo"},
	}
	for _, test := range tagTests {
		stringify := helperStringify(t, test.html)
		l := NewLexer(bytes.NewBufferString(test.html))
		for {
			tt, _ := l.Next()
			if tt == ErrorToken {
				assert.Equal(t, io.EOF, l.Err(), "error must be EOF in "+stringify)
				assert.Fail(t, "when error occurred we must be at the end in "+stringify)
				break
			} else if tt == StartTagToken || tt == EndTagToken || tt == DoctypeToken {
				assert.Equal(t, test.expected, string(l.Text()), "tags must match in "+stringify)
				break
			}
		}
	}
}

func TestAttributes(t *testing.T) {
	var attributeTests = []struct {
		attr     string
		expected []string
	}{
		{"<foo a=\"b\" />", []string{"a", "\"b\""}},
		{"<foo \nchecked \r\n value\r=\t'=/>\"' />", []string{"checked", "", "value", "'=/>\"'"}},
		{"<foo bar=\" a \n\t\r b \" />", []string{"bar", "\" a \n\t\r b \""}},
		{"<foo a/>", []string{"a", ""}},
		{"<foo /=/>", []string{"/", "/"}},

		// early endings
		{"<foo x", []string{"x", ""}},
		{"<foo x=", []string{"x", ""}},
		{"<foo x='", []string{"x", "'"}},
	}
	for _, test := range attributeTests {
		stringify := helperStringify(t, test.attr)
		l := NewLexer(bytes.NewBufferString(test.attr))
		i := 0
		for {
			tt, _ := l.Next()
			if tt == ErrorToken {
				assert.Equal(t, io.EOF, l.Err(), "error must be EOF in "+stringify)
				assert.Equal(t, len(test.expected), i, "when error occurred we must be at the end in "+stringify)
				break
			} else if tt == AttributeToken {
				assert.False(t, i+1 >= len(test.expected), "index must not exceed expected attributes size in "+stringify)
				if i+1 < len(test.expected) {
					assert.Equal(t, test.expected[i], string(l.Text()), "attribute keys must match at index "+strconv.Itoa(i)+" in "+stringify)
					assert.Equal(t, test.expected[i+1], string(l.AttrVal()), "attribute values must match at index "+strconv.Itoa(i)+" in "+stringify)
					i += 2
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////

var J int
var ss = [][]byte{
	[]byte(" style"),
	[]byte("style"),
	[]byte(" \r\n\tstyle"),
	[]byte("      style"),
	[]byte(" x"),
	[]byte("x"),
}

func BenchmarkWhitespace1(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, s := range ss {
			j := 0
			for {
				if c := s[j]; c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' {
					j++
				} else {
					break
				}
			}
			J += j
		}
	}
}

func BenchmarkWhitespace2(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, s := range ss {
			j := 0
			for {
				if c := s[j]; c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' {
					j++
					continue
				}
				break
			}
			J += j
		}
	}
}

func BenchmarkWhitespace3(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, s := range ss {
			j := 0
			for {
				if c := s[j]; c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f' {
					break
				}
				j++
			}
			J += j
		}
	}
}

////////////////////////////////////////////////////////////////

func ExampleNewLexer() {
	l := NewLexer(bytes.NewBufferString("John Doe"))
	out := ""
	for {
		tt, data := l.Next()
		if tt == ErrorToken {
			break
		}
		out += string(data)
		l.Free(len(data))
	}
	fmt.Println(out)
	// Output: John Doe
}
<commit_msg>Increased coverage<commit_after>package html // import "github.com/tdewolff/parse/html"

import (
	"bytes"
	"fmt"
	"io"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

func helperStringify(t *testing.T, input string) string {
	s := ""
	l := NewLexer(bytes.NewBufferString(input))
	for i := 0; i < 10; i++ {
		tt, data := l.Next()
		if tt == ErrorToken {
			s += tt.String() + "('" + l.Err().Error() + "')"
			break
		} else if tt == AttributeToken {
			s += tt.String() + "('" + string(data) + "=" + string(l.AttrVal()) + "') "
		} else {
			s += tt.String() + "('" + string(data) + "') "
		}
	}
	return s
}

////////////////////////////////////////////////////////////////

type TTs []TokenType

func TestTokens(t *testing.T) {
	var tokenTests = []struct {
		html     string
		expected TTs
	}{
		{"", TTs{StartTagToken, StartTagCloseToken, EndTagToken}},
		{"", TTs{StartTagToken, StartTagVoidToken}},
		{"", TTs{CommentToken}},
		{"</script>", TTs{StartTagToken, StartTagCloseToken, TextToken, EndTagToken, TextToken, EndTagToken}},
		{"';--></script>", TTs{StartTagToken, StartTagCloseToken, TextToken, EndTagToken}},
		{"", TTs{StartTagToken, StartTagCloseToken, TextToken, EndTagToken}},
		{"<![CDATA[ test ]]>", TTs{TextToken}},

		// early endings
		{"", TTs{StartTagToken, StartTagCloseToken, TextToken}},

		// go-fuzz
		{"</>", TTs{EndTagToken}},
	}
	for _, test := range tokenTests {
		expected := []TokenType(test.expected)
		stringify := helperStringify(t, test.html)
		l := NewLexer(bytes.NewBufferString(test.html))
		i := 0
		for {
			tt, _ := l.Next()
			if tt == ErrorToken {
				assert.Equal(t, io.EOF, l.Err(), "error must be EOF in "+stringify)
				assert.Equal(t, len(expected), i, "when error occurred we must be at the end in "+stringify)
				break
			}
			assert.False(t, i >= len(expected), "index must not exceed expected token types size in "+stringify)
			if i < len(expected) {
				assert.Equal(t, expected[i], tt, "token types must match at index "+strconv.Itoa(i)+" in "+stringify)
			}
			i++
		}
	}

	assert.Equal(t, "Invalid(100)", TokenType(100).String())
}

func TestTags(t *testing.T) {
	var tagTests = []struct {
		html     string
		expected string
	}{
		{"<foo:bar.qux-norf/>", "foo:bar.qux-norf"},
		{"<foo?bar/qux>", "foo?bar/qux"},
		{"<!DOCTYPE note SYSTEM \"Note.dtd\">", " note SYSTEM \"Note.dtd\""},
		{"</foo >", "foo"},

		// early endings
		{"<foo ", "foo"},
	}
	for _, test := range tagTests {
		stringify := helperStringify(t, test.html)
		l := NewLexer(bytes.NewBufferString(test.html))
		for {
			tt, _ := l.Next()
			if tt == ErrorToken {
				assert.Equal(t, io.EOF, l.Err(), "error must be EOF in "+stringify)
				assert.Fail(t, "when error occurred we must be at the end in "+stringify)
				break
			} else if tt == StartTagToken || tt == EndTagToken || tt == DoctypeToken {
				assert.Equal(t, test.expected, string(l.Text()), "tags must match in "+stringify)
				break
			}
		}
	}
}

func TestAttributes(t *testing.T) {
	var attributeTests = []struct {
		attr     string
		expected []string
	}{
		{"<foo a=\"b\" />", []string{"a", "\"b\""}},
		{"<foo \nchecked \r\n value\r=\t'=/>\"' />", []string{"checked", "", "value", "'=/>\"'"}},
		{"<foo bar=\" a \n\t\r b \" />", []string{"bar", "\" a \n\t\r b \""}},
		{"<foo a/>", []string{"a", ""}},
		{"<foo /=/>", []string{"/", "/"}},

		// early endings
		{"<foo x", []string{"x", ""}},
		{"<foo x=", []string{"x", ""}},
		{"<foo x='", []string{"x", "'"}},
	}
	for _, test := range attributeTests {
		stringify := helperStringify(t, test.attr)
		l := NewLexer(bytes.NewBufferString(test.attr))
		i := 0
		for {
			tt, _ := l.Next()
			if tt == ErrorToken {
				assert.Equal(t, io.EOF, l.Err(), "error must be EOF in "+stringify)
				assert.Equal(t, len(test.expected), i, "when error occurred we must be at the end in "+stringify)
				break
			} else if tt == AttributeToken {
				assert.False(t, i+1 >= len(test.expected), "index must not exceed expected attributes size in "+stringify)
				if i+1 < len(test.expected) {
					assert.Equal(t, test.expected[i], string(l.Text()), "attribute keys must match at index "+strconv.Itoa(i)+" in "+stringify)
					assert.Equal(t, test.expected[i+1], string(l.AttrVal()), "attribute values must match at index "+strconv.Itoa(i)+" in "+stringify)
					i += 2
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////

var J int
var ss = [][]byte{
	[]byte(" style"),
	[]byte("style"),
	[]byte(" \r\n\tstyle"),
	[]byte("      style"),
	[]byte(" x"),
	[]byte("x"),
}

func BenchmarkWhitespace1(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, s := range ss {
			j := 0
			for {
				if c := s[j]; c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' {
					j++
				} else {
					break
				}
			}
			J += j
		}
	}
}

func BenchmarkWhitespace2(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, s := range ss {
			j := 0
			for {
				if c := s[j]; c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' {
					j++
					continue
				}
				break
			}
			J += j
		}
	}
}

func BenchmarkWhitespace3(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, s := range ss {
			j := 0
			for {
				if c := s[j]; c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f' {
					break
				}
				j++
			}
			J += j
		}
	}
}

////////////////////////////////////////////////////////////////

func ExampleNewLexer() {
	l := NewLexer(bytes.NewBufferString("John Doe"))
	out := ""
	for {
		tt, data := l.Next()
		if tt == ErrorToken {
			break
		}
		out += string(data)
		l.Free(len(data))
	}
	fmt.Println(out)
	// Output: John Doe
}
<|endoftext|>


<commit_before>package qemu

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath("qemu-system-x86_64")
	if err != nil {
		return nil
	}

	return &QemuDriver{
		executable: cmd,
	}
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		qmpSockName: homeDir + QmpSockName,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case int:
			proc, err = os.FindProcess(p.(int))
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(ctx, qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.wdt <- "quit"
	_ = <-qc.waitQmp
	close(qc.waitQmp)
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, cmd *hypervisor.PauseCommand) {
	commands := make([]*QmpCommand, 1)

	if cmd.Pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	// TODO: handle qmp error
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  defaultRespond(ctx, &hypervisor.PauseResult{Reply: cmd}),
	}

}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.BlockDescriptor) {
	name := blockInfo.Name
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(ctx, qc, name, sourceType, filename, format, id)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.BlockDescriptor, callback hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(ctx, qc, id, callback)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo) {
	newNetworkAddSession(ctx, qc, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent) {
	newNetworkDelSession(ctx, qc, n.DeviceName, callback)
}

func (qc *QemuContext) AddCpu(ctx *hypervisor.VmContext, id int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 1)
	commands[0] = &QmpCommand{
		Execute: "cpu-add",
		Arguments: map[string]interface{}{
			"id": id,
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  defaultRespond(ctx, callback),
	}
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  defaultRespond(ctx, callback),
	}
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuContext) arguments(ctx *hypervisor.VmContext) []string {
	if ctx.Boot == nil {
		ctx.Boot = &hypervisor.BootConfig{
			CPU:    1,
			Memory: 128,
			Kernel: hypervisor.DefaultKernel,
			Initrd: hypervisor.DefaultInitrd,
		}
	}
	boot := ctx.Boot

	var machineClass, memParams, cpuParams string
	if ctx.Boot.HotAddCpuMem {
		machineClass = "pc-i440fx-2.1"
		memParams = fmt.Sprintf("size=%d,slots=1,maxmem=%s", ctx.Boot.Memory, hypervisor.DefaultMaxMem) // TODO set maxmem to the total memory of the system
		cpuParams = fmt.Sprintf("cpus=%d,maxcpus=%d", ctx.Boot.CPU, hypervisor.DefaultMaxCpus)          // TODO set it to the cpus of the system
	} else {
		machineClass = "pc-i440fx-2.0"
		memParams = strconv.Itoa(ctx.Boot.Memory)
		cpuParams = strconv.Itoa(ctx.Boot.CPU)
	}

	params := []string{
		"-machine", machineClass + ",accel=kvm,usb=off", "-global", "kvm-pit.lost_tick_policy=discard", "-cpu", "host"}
	if _, err := os.Stat("/dev/kvm"); os.IsNotExist(err) {
		glog.V(1).Info("kvm not exist change to no kvm mode")
		params = []string{"-machine", machineClass + ",usb=off", "-cpu", "core2duo"}
	}

	if boot.Bios != "" && boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Bios),
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else if boot.Bios != "" {
		params = append(params,
			"-bios", boot.Bios,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	} else if boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else {
		params = append(params,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	}

	return append(params,
		"-realtime", "mlock=off", "-no-user-config", "-nodefaults", "-no-hpet",
		"-rtc", "base=utc,driftfix=slew", "-no-reboot", "-display", "none", "-boot", "strict=on",
		"-m", memParams, "-smp", cpuParams,
		"-qmp", fmt.Sprintf("unix:%s,server,nowait", qc.qmpSockName), "-serial", fmt.Sprintf("unix:%s,server,nowait", ctx.ConsoleSockName),
		"-device", "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x2", "-device", "virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3",
		"-chardev", fmt.Sprintf("socket,id=charch0,path=%s,server,nowait", ctx.HyperSockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charch0,id=channel0,name=sh.hyper.channel.0",
		"-chardev", fmt.Sprintf("socket,id=charch1,path=%s,server,nowait", ctx.TtySockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=2,chardev=charch1,id=channel1,name=sh.hyper.channel.1",
		"-fsdev", fmt.Sprintf("local,id=virtio9p,path=%s,security_model=none", ctx.ShareDir),
		"-device", fmt.Sprintf("virtio-9p-pci,fsdev=virtio9p,mount_tag=%s", hypervisor.ShareDirTag),
	)
}
<commit_msg>qemu: handle the pause() error as the same as libvirt driver<commit_after>package qemu

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath("qemu-system-x86_64")
	if err != nil {
		return nil
	}

	return &QemuDriver{
		executable: cmd,
	}
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		qmpSockName: homeDir + QmpSockName,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case int:
			proc, err = os.FindProcess(p.(int))
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(ctx, qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.wdt <- "quit"
	_ = <-qc.waitQmp
	close(qc.waitQmp)
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, cmd *hypervisor.PauseCommand) {
	commands := make([]*QmpCommand, 1)

	if cmd.Pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			cause := ""
			if err != nil {
				cause = err.Error()
			}

			ctx.Hub <- &hypervisor.PauseResult{Cause: cause, Reply: cmd}
		},
	}

}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.BlockDescriptor) {
	name := blockInfo.Name
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(ctx, qc, name, sourceType, filename, format, id)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.BlockDescriptor, callback hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(ctx, qc, id, callback)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo) {
	newNetworkAddSession(ctx, qc, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent) {
	newNetworkDelSession(ctx, qc, n.DeviceName, callback)
}

func (qc *QemuContext) AddCpu(ctx *hypervisor.VmContext, id int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 1)
	commands[0] = &QmpCommand{
		Execute: "cpu-add",
		Arguments: map[string]interface{}{
			"id": id,
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  defaultRespond(ctx, callback),
	}
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int, callback hypervisor.VmEvent) {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  defaultRespond(ctx, callback),
	}
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuContext) arguments(ctx *hypervisor.VmContext) []string {
	if ctx.Boot == nil {
		ctx.Boot = &hypervisor.BootConfig{
			CPU:    1,
			Memory: 128,
			Kernel: hypervisor.DefaultKernel,
			Initrd: hypervisor.DefaultInitrd,
		}
	}
	boot := ctx.Boot

	var machineClass, memParams, cpuParams string
	if ctx.Boot.HotAddCpuMem {
		machineClass = "pc-i440fx-2.1"
		memParams = fmt.Sprintf("size=%d,slots=1,maxmem=%s", ctx.Boot.Memory, hypervisor.DefaultMaxMem) // TODO set maxmem to the total memory of the system
		cpuParams = fmt.Sprintf("cpus=%d,maxcpus=%d", ctx.Boot.CPU, hypervisor.DefaultMaxCpus)          // TODO set it to the cpus of the system
	} else {
		machineClass = "pc-i440fx-2.0"
		memParams = strconv.Itoa(ctx.Boot.Memory)
		cpuParams = strconv.Itoa(ctx.Boot.CPU)
	}

	params := []string{
		"-machine", machineClass + ",accel=kvm,usb=off", "-global", "kvm-pit.lost_tick_policy=discard", "-cpu", "host"}
	if _, err := os.Stat("/dev/kvm"); os.IsNotExist(err) {
		glog.V(1).Info("kvm not exist change to no kvm mode")
		params = []string{"-machine", machineClass + ",usb=off", "-cpu", "core2duo"}
	}

	if boot.Bios != "" && boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Bios),
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else if boot.Bios != "" {
		params = append(params,
			"-bios", boot.Bios,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	} else if boot.Cbfs != "" {
		params = append(params,
			"-drive", fmt.Sprintf("if=pflash,file=%s,readonly=on", boot.Cbfs))
	} else {
		params = append(params,
			"-kernel", boot.Kernel, "-initrd", boot.Initrd, "-append", "\"console=ttyS0 panic=1 no_timer_check\"")
	}

	return append(params,
		"-realtime", "mlock=off", "-no-user-config", "-nodefaults", "-no-hpet",
		"-rtc", "base=utc,driftfix=slew", "-no-reboot", "-display", "none", "-boot", "strict=on",
		"-m", memParams, "-smp", cpuParams,
		"-qmp", fmt.Sprintf("unix:%s,server,nowait", qc.qmpSockName), "-serial", fmt.Sprintf("unix:%s,server,nowait", ctx.ConsoleSockName),
		"-device", "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x2", "-device", "virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3",
		"-chardev", fmt.Sprintf("socket,id=charch0,path=%s,server,nowait", ctx.HyperSockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charch0,id=channel0,name=sh.hyper.channel.0",
		"-chardev", fmt.Sprintf("socket,id=charch1,path=%s,server,nowait", ctx.TtySockName),
		"-device", "virtserialport,bus=virtio-serial0.0,nr=2,chardev=charch1,id=channel1,name=sh.hyper.channel.1",
		"-fsdev", fmt.Sprintf("local,id=virtio9p,path=%s,security_model=none", ctx.ShareDir),
		"-device", fmt.Sprintf("virtio-9p-pci,fsdev=virtio9p,mount_tag=%s", hypervisor.ShareDirTag),
	)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
	"strings"

	"github.com/coreos/go-oidc/jose"
	"github.com/coreos/go-oidc/oauth2"
	"github.com/samuel/go-zookeeper/zk"
	"github.com/stratio/paas-oauth/common"
	"golang.org/x/net/context"
)

type loginRequest struct {
	Uid string `json:"uid,omitempty"`

	Password string `json:"password,omitempty"`

	Token string `json:"token,omitempty"`
}

type loginResponse struct {
	Token string `json:"token,omitempty"`
}

type secondStruct struct {
	Mail string `json:"mail"`
}

type testStruct struct {
	Clip []secondStruct `json:"attributes"`
}

func handleLogin(ctx context.Context, w http.ResponseWriter, r *http.Request) *common.HttpError {
	code := r.URL.Query()["code"]
	log.Printf("Code: %s", code)

	o2cli := oauth2Client(ctx)

	token, err := o2cli.RequestToken(oauth2.GrantTypeAuthCode, code[0])

	if err != nil {
		log.Print("error %w", err)
	}

	log.Printf("Token: %+v", token)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	profileUrl := ctx.Value("oauth-profile-url").(string) + token.AccessToken

	log.Printf("Getting profile: %s", profileUrl)

	resp, err := client.Get(profileUrl)

	if err != nil {
		log.Print("error %w", err)
	}

	defer resp.Body.Close()

	contents, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		log.Print("error %w", err)
	}

	log.Printf("Profile: %s", contents)

	var um testStruct
	err = json.Unmarshal([]byte(contents), &um)

	if err != nil {
		log.Print("error %w", err)
	}

	uid := um.Clip[1].Mail
	c := ctx.Value("zk").(*zk.Conn)

	users, _, err := c.Children("/dcos/users")
	if err != nil && err != zk.ErrNoNode {
		return common.NewHttpError("invalid email", http.StatusInternalServerError)
	}

	userPath := fmt.Sprintf("/dcos/users/%s", uid)
	if len(users) == 0 {
		// create first user
		log.Printf("creating first user %v", uid)
		err = common.CreateParents(c, userPath, []byte(uid))
		if err != nil {
			return common.NewHttpError("Zookeeper error", http.StatusInternalServerError)
		}
	}

	exists, _, err := c.Exists(userPath)
	if err != nil || !exists {
		return common.NewHttpError("User unauthorized", http.StatusUnauthorized)
	}

	claims := jose.Claims{
		"uid": uid,
	}

	secretKey, _ := ctx.Value("secret-key").([]byte)

	clusterToken, err := jose.NewSignedJWT(claims, jose.NewSignerHMAC("secret", secretKey))
	if err != nil {
		return common.NewHttpError("JWT creation error", http.StatusInternalServerError)
	}
	encodedClusterToken := clusterToken.Encode()

	const cookieMaxAge = 388800
	// required for IE 6, 7 and 8
	expiresTime := time.Now().Add(cookieMaxAge * time.Second)

	authCookie := &http.Cookie{
		Name:     "dcos-acs-auth-cookie",
		Value:    encodedClusterToken,
		Path:     "/",
		HttpOnly: true,
		Expires:  expiresTime,
		MaxAge:   cookieMaxAge,
	}
	http.SetCookie(w, authCookie)

	user := User{
		Uid:         uid,
		Description: uid,
		IsRemote:    false,
	}
	userBytes, err := json.Marshal(user)
	if err != nil {
		log.Printf("Marshal: %v", err)
		return common.NewHttpError("JSON marshalling failed", http.StatusInternalServerError)
	}
	infoCookie := &http.Cookie{
		Name:    "dcos-acs-info-cookie",
		Value:   base64.URLEncoding.EncodeToString(userBytes),
		Path:    "/",
		Expires: expiresTime,
		MaxAge:  cookieMaxAge,
	}
	http.SetCookie(w, infoCookie)

	//json.NewEncoder(w).Encode(loginResponse{Token: encodedClusterToken})

	http.Redirect(w, r, "http://"+strings.Split(r.Host,":")[0], http.StatusMovedPermanently)

	return nil
}

func handleLogout(ctx context.Context, w http.ResponseWriter, r *http.Request) *common.HttpError {
	// required for IE 6, 7 and 8
	expiresTime := time.Unix(1, 0)

	for _, name := range []string{"dcos-acs-auth-cookie", "dcos-acs-info-cookie"} {
		cookie := &http.Cookie{
			Name:     name,
			Value:    "",
			Path:     "/",
			HttpOnly: true,
			Expires:  expiresTime,
			MaxAge:   -1,
		}

		http.SetCookie(w, cookie)
	}

	return nil
}

func oauth2Client(ctx context.Context) *oauth2.Client {
	key := ctx.Value("oauth-app-key").(string)
	secret := ctx.Value("oauth-app-secret").(string)
	tokenUrl := ctx.Value("oauth-token-url").(string)
	authUrl := ctx.Value("oauth-auth-url").(string)
	callbackUrl := ctx.Value("oauth-callback-url").(string)

	conf := oauth2.Config{
		Credentials: oauth2.ClientCredentials{ID: key, Secret: secret},
		TokenURL:    tokenUrl,
		AuthMethod:  oauth2.AuthMethodClientSecretBasic,
		RedirectURL: callbackUrl,
		AuthURL:     authUrl,
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	o2cli, _ := oauth2.NewClient(client, conf)
	return o2cli
}
<commit_msg>Redirect correction<commit_after>package main

import (
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
	"strings"

	"github.com/coreos/go-oidc/jose"
	"github.com/coreos/go-oidc/oauth2"
	"github.com/samuel/go-zookeeper/zk"
	"github.com/stratio/paas-oauth/common"
	"golang.org/x/net/context"
)

type loginRequest struct {
	Uid string `json:"uid,omitempty"`

	Password string `json:"password,omitempty"`

	Token string `json:"token,omitempty"`
}

type loginResponse struct {
	Token string `json:"token,omitempty"`
}

type secondStruct struct {
	Mail string `json:"mail"`
}

type testStruct struct {
	Clip []secondStruct `json:"attributes"`
}

func handleLogin(ctx context.Context, w http.ResponseWriter, r *http.Request) *common.HttpError {
	code := r.URL.Query()["code"]
	log.Printf("Code: %s", code)

	o2cli := oauth2Client(ctx)

	token, err := o2cli.RequestToken(oauth2.GrantTypeAuthCode, code[0])

	if err != nil {
		log.Print("error %w", err)
	}

	log.Printf("Token: %+v", token)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	profileUrl := ctx.Value("oauth-profile-url").(string) + token.AccessToken

	log.Printf("Getting profile: %s", profileUrl)

	resp, err := client.Get(profileUrl)

	if err != nil {
		log.Print("error %w", err)
	}

	defer resp.Body.Close()

	contents, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		log.Print("error %w", err)
	}

	log.Printf("Profile: %s", contents)

	var um testStruct
	err = json.Unmarshal([]byte(contents), &um)

	if err != nil {
		log.Print("error %w", err)
	}

	uid := um.Clip[1].Mail
	c := ctx.Value("zk").(*zk.Conn)

	users, _, err := c.Children("/dcos/users")
	if err != nil && err != zk.ErrNoNode {
		return common.NewHttpError("invalid email", http.StatusInternalServerError)
	}

	userPath := fmt.Sprintf("/dcos/users/%s", uid)
	if len(users) == 0 {
		// create first user
		log.Printf("creating first user %v", uid)
		err = common.CreateParents(c, userPath, []byte(uid))
		if err != nil {
			return common.NewHttpError("Zookeeper error", http.StatusInternalServerError)
		}
	}

	exists, _, err := c.Exists(userPath)
	if err != nil || !exists {
		return common.NewHttpError("User unauthorized", http.StatusUnauthorized)
	}

	claims := jose.Claims{
		"uid": uid,
	}

	secretKey, _ := ctx.Value("secret-key").([]byte)

	clusterToken, err := jose.NewSignedJWT(claims, jose.NewSignerHMAC("secret", secretKey))
	if err != nil {
		return common.NewHttpError("JWT creation error", http.StatusInternalServerError)
	}
	encodedClusterToken := clusterToken.Encode()

	const cookieMaxAge = 388800
	// required for IE 6, 7 and 8
	expiresTime := time.Now().Add(cookieMaxAge * time.Second)

	authCookie := &http.Cookie{
		Name:     "dcos-acs-auth-cookie",
		Value:    encodedClusterToken,
		Path:     "/",
		HttpOnly: true,
		Expires:  expiresTime,
		MaxAge:   cookieMaxAge,
	}
	http.SetCookie(w, authCookie)

	user := User{
		Uid:         uid,
		Description: uid,
		IsRemote:    false,
	}
	userBytes, err := json.Marshal(user)
	if err != nil {
		log.Printf("Marshal: %v", err)
		return common.NewHttpError("JSON marshalling failed", http.StatusInternalServerError)
	}
	infoCookie := &http.Cookie{
		Name:    "dcos-acs-info-cookie",
		Value:   base64.URLEncoding.EncodeToString(userBytes),
		Path:    "/",
		Expires: expiresTime,
		MaxAge:  cookieMaxAge,
	}
	http.SetCookie(w, infoCookie)

	//json.NewEncoder(w).Encode(loginResponse{Token: encodedClusterToken})

	http.Redirect(w, r, "https://"+strings.Split(r.Host,":")[0], http.StatusFound)

	return nil
}

func handleLogout(ctx context.Context, w http.ResponseWriter, r *http.Request) *common.HttpError {
	// required for IE 6, 7 and 8
	expiresTime := time.Unix(1, 0)

	for _, name := range []string{"dcos-acs-auth-cookie", "dcos-acs-info-cookie"} {
		cookie := &http.Cookie{
			Name:     name,
			Value:    "",
			Path:     "/",
			HttpOnly: true,
			Expires:  expiresTime,
			MaxAge:   -1,
		}

		http.SetCookie(w, cookie)
	}

	return nil
}

func oauth2Client(ctx context.Context) *oauth2.Client {
	key := ctx.Value("oauth-app-key").(string)
	secret := ctx.Value("oauth-app-secret").(string)
	tokenUrl := ctx.Value("oauth-token-url").(string)
	authUrl := ctx.Value("oauth-auth-url").(string)
	callbackUrl := ctx.Value("oauth-callback-url").(string)

	conf := oauth2.Config{
		Credentials: oauth2.ClientCredentials{ID: key, Secret: secret},
		TokenURL:    tokenUrl,
		AuthMethod:  oauth2.AuthMethodClientSecretBasic,
		RedirectURL: callbackUrl,
		AuthURL:     authUrl,
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	o2cli, _ := oauth2.NewClient(client, conf)
	return o2cli
}
<|endoftext|>


<commit_before>// Package metric provides functions for computing metrics.
package metric

import (
	"math"
)

// L2 computes the Euclidean distance between two n-element vectors in the
// n-dimensional Euclidean space.
func L2(x, y []float64) float64 {
	var sum, delta float64

	for i := range x {
		delta = x[i] - y[i]
		sum += delta * delta
	}

	return math.Sqrt(sum)
}
<commit_msg>Love UTF-8!<commit_after>// Package metric provides functions for computing metrics.
package metric

import (
	"math"
)

// L2 computes the Euclidean distance between two n-element vectors in the
// n-dimensional Euclidean space.
func L2(x, y []float64) float64 {
	var sum, Δ float64

	for i := range x {
		Δ = x[i] - y[i]
		sum += Δ * Δ
	}

	return math.Sqrt(sum)
}
<|endoftext|>