<commit_before>/*
Copyright 2015 Google Inc. 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 mirror provides two-way synchronization of metadata stored in git-notes and Phabricator.
package mirror

import (
	"github.com/google/git-appraise/repository"
	"github.com/google/git-appraise/review"
	"github.com/google/git-appraise/review/comment"
	"github.com/google/git-phabricator-mirror/mirror/arcanist"
	review_utils "github.com/google/git-phabricator-mirror/mirror/review"
	"log"
)

var arc = arcanist.Arcanist{}

// processedStates is used to keep track of the state of each repository at the last time we processed it.
// That, in turn, is used to avoid re-processing a repo if its state has not changed.
var processedStates = make(map[string]string)
var existingComments = make(map[string][]review.CommentThread)
var openReviews = make(map[string][]review_utils.PhabricatorReview)

func hasOverlap(newComment comment.Comment, existingComments []review.CommentThread) bool {
	for _, existing := range existingComments {
		if review_utils.Overlaps(newComment, existing.Comment) {
			return true
		} else if hasOverlap(newComment, existing.Children) {
			return true
		}
	}
	return false
}

func mirrorRepoToReview(repo repository.Repo, tool review_utils.Tool, syncToRemote bool) {
	if syncToRemote {
		repo.PullNotes("origin", "refs/notes/devtools/*")
	}

	stateHash, err := repo.GetRepoStateHash()
	if err != nil {
		log.Fatal(err)
	}
	if processedStates[repo.GetPath()] != stateHash {
		log.Print("Mirroring repo: ", repo)
		for _, r := range review.ListAll(repo) {
			reviewJson, err := r.GetJson()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Mirroring review: ", reviewJson)
			existingComments[r.Revision] = r.Comments
			tool.EnsureRequestExists(repo, r)
		}
		openReviews[repo.GetPath()] = tool.ListOpenReviews(repo)
		processedStates[repo.GetPath()] = stateHash
		tool.Refresh(repo)
	}
	for _, phabricatorReview := range openReviews[repo.GetPath()] {
		if reviewCommit := phabricatorReview.GetFirstCommit(repo); reviewCommit != "" {
			log.Println("Processing review: ", reviewCommit)
			revisionComments := existingComments[reviewCommit]
			log.Printf("Loaded %d comments for %v\n", len(revisionComments), reviewCommit)
			for _, c := range phabricatorReview.LoadComments() {
				if !hasOverlap(c, revisionComments) {
					// The comment is new.
					note, err := c.Write()
					if err != nil {
						log.Fatal(err)
					}
					log.Printf("Appending a comment: %s", string(note))
					repo.AppendNote(comment.Ref, reviewCommit, note)
				} else {
					log.Printf("Skipping '%v', as it has already been written\n", c)
				}
			}
		}
	}
	if syncToRemote {
		if err := repo.PushNotes("origin", "refs/notes/devtools/*"); err != nil {
			log.Printf("Failed to push updates to the repo %v: %v\n", repo, err)
		}
	}
}

// Repo mirrors the given repository using the system-wide installation of
// the "arcanist" command line tool.
func Repo(repo repository.Repo, syncToRemote bool) {
	mirrorRepoToReview(repo, arc, syncToRemote)
}
<commit_msg>Incorporate the new change to the git-appraise library to have the ListAll method return instances of ReviewSummary instead of Review.<commit_after>/*
Copyright 2015 Google Inc. 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 mirror provides two-way synchronization of metadata stored in git-notes and Phabricator.
package mirror

import (
	"github.com/google/git-appraise/repository"
	"github.com/google/git-appraise/review"
	"github.com/google/git-appraise/review/comment"
	"github.com/google/git-phabricator-mirror/mirror/arcanist"
	review_utils "github.com/google/git-phabricator-mirror/mirror/review"
	"log"
)

var arc = arcanist.Arcanist{}

// processedStates is used to keep track of the state of each repository at the last time we processed it.
// That, in turn, is used to avoid re-processing a repo if its state has not changed.
var processedStates = make(map[string]string)
var existingComments = make(map[string][]review.CommentThread)
var openReviews = make(map[string][]review_utils.PhabricatorReview)

func hasOverlap(newComment comment.Comment, existingComments []review.CommentThread) bool {
	for _, existing := range existingComments {
		if review_utils.Overlaps(newComment, existing.Comment) {
			return true
		} else if hasOverlap(newComment, existing.Children) {
			return true
		}
	}
	return false
}

func mirrorRepoToReview(repo repository.Repo, tool review_utils.Tool, syncToRemote bool) {
	if syncToRemote {
		repo.PullNotes("origin", "refs/notes/devtools/*")
	}

	stateHash, err := repo.GetRepoStateHash()
	if err != nil {
		log.Fatal(err)
	}
	if processedStates[repo.GetPath()] != stateHash {
		log.Print("Mirroring repo: ", repo)
		for _, r := range review.ListAll(repo) {
			reviewJson, err := r.GetJson()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Mirroring review: ", reviewJson)
			existingComments[r.Revision] = r.Comments
			reviewDetails, err := r.Details()
			if err == nil {
				tool.EnsureRequestExists(repo, *reviewDetails)
			}
		}
		openReviews[repo.GetPath()] = tool.ListOpenReviews(repo)
		processedStates[repo.GetPath()] = stateHash
		tool.Refresh(repo)
	}
	for _, phabricatorReview := range openReviews[repo.GetPath()] {
		if reviewCommit := phabricatorReview.GetFirstCommit(repo); reviewCommit != "" {
			log.Println("Processing review: ", reviewCommit)
			revisionComments := existingComments[reviewCommit]
			log.Printf("Loaded %d comments for %v\n", len(revisionComments), reviewCommit)
			for _, c := range phabricatorReview.LoadComments() {
				if !hasOverlap(c, revisionComments) {
					// The comment is new.
					note, err := c.Write()
					if err != nil {
						log.Fatal(err)
					}
					log.Printf("Appending a comment: %s", string(note))
					repo.AppendNote(comment.Ref, reviewCommit, note)
				} else {
					log.Printf("Skipping '%v', as it has already been written\n", c)
				}
			}
		}
	}
	if syncToRemote {
		if err := repo.PushNotes("origin", "refs/notes/devtools/*"); err != nil {
			log.Printf("Failed to push updates to the repo %v: %v\n", repo, err)
		}
	}
}

// Repo mirrors the given repository using the system-wide installation of
// the "arcanist" command line tool.
func Repo(repo repository.Repo, syncToRemote bool) {
	mirrorRepoToReview(repo, arc, syncToRemote)
}
<|endoftext|>


<commit_before>package tsne4go

import (
	"math"
	"sync"
)

const (
	defaultPerplexity = 30
	defaultDim        = 2
	defaultEpsilon    = 10 // was 10
)

type Point [defaultDim]float64

type TSne struct {
	perplexity float64
	dim        int
	epsilon    float64
	iter       int
	length     int
	probas     []float64
	Solution   []Point
	gains      []Point
	ystep      []Point
	// Meta-information about each point, if needed
	// It is useful to associate, for instance, a label with each point
	// The algorithm dosen't take this information into consideration
	// It can be anything
	// It can even be nil, if the user has no need for it
	Meta []interface{}
}

// New takes a set of Distancer instances
// and creates matrix P from them using gaussian kernel
// Meta-information is provided here
// It is under the programmer's responsibility :
// it can be nil if no meta information is needed, or anything else
func New(x Distancer, meta []interface{}) *TSne {
	dists := xtod(x) // convert x to distances using gaussian kernel
	tsne := &TSne{
		defaultPerplexity,    // perplexity
		defaultDim,           // dim
		defaultEpsilon,       // epsilon
		0,                    // iters
		x.Len(),              //length
		d2p(dists, 30, 1e-4), // probas
		nil,                  // Solution
		nil,                  // gains
		nil,                  // ystep
		meta,                 // Meta
	}
	tsne.initSolution() // refresh this
	return tsne
}

// (re)initializes the solution to random
func (tsne *TSne) initSolution() {
	// generate random solution to t-SNE
	tsne.Solution = randn2d(tsne.length)  // the solution
	tsne.gains = fill2d(tsne.length, 1.0) // step gains to accelerate progress in unchanging directions
	tsne.ystep = fill2d(tsne.length, 0.0) // momentum accumulator
	tsne.iter = 0
}

// perform a single step of optimization to improve the embedding
func (tsne *TSne) Step() float64 {
	tsne.iter++
	length := tsne.length
	cost, grad := tsne.costGrad(tsne.Solution) // evaluate gradient
	//ymean := make([]float64, tsne.dim)
	var ymean [defaultDim]float64
	var wg sync.WaitGroup
	// perform gradient step
	for i := 0; i < length; i++ {
		go func(i int) {
			wg.Add(1)
			defer wg.Done()
			for d := 0; d < tsne.dim; d++ {
				gid := grad[i][d]
				sid := tsne.ystep[i][d]
				gainid := tsne.gains[i][d]
				// compute gain update
				if sign(gid) == sign(sid) {
					tsne.gains[i][d] = gainid * 0.8
				} else {
					tsne.gains[i][d] = gainid + 0.2
				}
				// compute momentum step direction
				var momval float64
				if tsne.iter < 250 {
					momval = 0.5
				} else {
					momval = 0.8
				}
				newsid := momval*sid - tsne.epsilon*tsne.gains[i][d]*grad[i][d]
				tsne.ystep[i][d] = newsid // remember the step we took
				// step!
				tsne.Solution[i][d] += newsid
				ymean[d] += tsne.Solution[i][d] // accumulate mean so that we can center later
			}
		}(i)
	}
	wg.Wait()
	// reproject Y to be zero mean
	for i := 0; i < length; i++ {
		for d := 0; d < tsne.dim; d++ {
			tsne.Solution[i][d] -= ymean[d] / float64(length)
		}
	}
	return cost
}

// return cost and gradient, given an arrangement
func (tsne *TSne) costGrad(Y []Point) (cost float64, grad []Point) {
	length := tsne.length
	dim := tsne.dim // dim of output space
	P := tsne.probas
	var pmul float64
	if tsne.iter < 100 { // trick that helps with local optima
		pmul = 4.0
	} else {
		pmul = 1.0
	}
	// compute current Q distribution, unnormalized first
	Qu := make([]float64, length*length)
	qsum := 0.0
	for i := 0; i < length-1; i++ {
		for j := i + 1; j < length; j++ {
			dsum := 0.0
			for d := 0; d < dim; d++ {
				dhere := Y[i][d] - Y[j][d]
				dsum += dhere * dhere
			}
			qu := 1.0 / (1.0 + dsum) // Student t-distribution
			Qu[i*length+j] = qu
			Qu[j*length+i] = qu
			qsum += 2 * qu
		}
	}
	// normalize Q distribution to sum to 1
	squareLength := length * length
	Q := make([]float64, squareLength)
	for q := range Q {
		Q[q] = math.Max(Qu[q]/qsum, 1e-100)
	}
	cost = 0.0
	grad = []Point{}
	for i := 0; i < length; i++ {
		//gsum := make(Point, dim) // init grad for point i
		var gsum Point
		for j := 0; j < length; j++ {
			// accumulate cost (the non-constant portion at least...)
			cost += -P[i*length+j] * math.Log(Q[i*length+j])
			premult := 4 * (pmul*P[i*length+j] - Q[i*length+j]) * Qu[i*length+j]
			for d := 0; d < dim; d++ {
				gsum[d] += premult * (Y[i][d] - Y[j][d])
			}
		}
		grad = append(grad, gsum)
	}

	return cost, grad
}

// Normalize makes all values from the solution in the interval [0; 1]
func (tsne *TSne) NormalizeSolution() {
	mins := make([]float64, tsne.dim)
	maxs := make([]float64, tsne.dim)
	for i, pt := range tsne.Solution {
		for j, val := range pt {
			if i == 0 || val < mins[j] {
				mins[j] = val
			}
			if i == 0 || val > maxs[j] {
				maxs[j] = val
			}
		}
	}
	for i, pt := range tsne.Solution {
		for j, val := range pt {
			tsne.Solution[i][j] = (val - mins[j]) / (maxs[j] - mins[j])
		}
	}
}
<commit_msg>simplifications, code is a little faster<commit_after>package tsne4go

import (
	"math"
	"sync"
)

const (
	perplexity = 30
	nbDims     = 2
	epsilon    = 10
)

type Point [nbDims]float64

type TSne struct {
	iter int
	// All subsequent vector should have 'length' elements
	length   int
	probas   []float64
	Solution []Point
	gains    []Point
	ystep    []Point
	// Meta-information about each point, if needed
	// It is useful to associate, for instance, a label with each point
	// The algorithm dosen't take this information into consideration
	// It can be anything, even nil if the user has no need for it
	Meta []interface{}
}

// New takes a set of Distancer instances
// and creates matrix P from them using gaussian kernel
// Meta-information is provided here
// It is under the programmer's responsibility :
// it can be nil if no meta information is needed, or anything else
func New(x Distancer, meta []interface{}) *TSne {
	dists := xtod(x) // convert x to distances using gaussian kernel
	length := x.Len()
	tsne := &TSne{
		0,      // iters
		length, // length
		d2p(dists, perplexity, 1e-4), // probas
		randn2d(length),              // Solution
		fill2d(length, 1.0),          // gains
		fill2d(length, 0.0),          // ystep
		meta,                         // Meta
	}
	return tsne
}

// perform a single step of optimization to improve the embedding
func (tsne *TSne) Step() float64 {
	tsne.iter++
	length := tsne.length
	cost, grad := tsne.costGrad(tsne.Solution) // evaluate gradient
	var ymean [nbDims]float64
	var wg sync.WaitGroup
	// perform gradient step
	for i := 0; i < length; i++ {
		go func(i int) {
			wg.Add(1)
			defer wg.Done()
			for d := 0; d < nbDims; d++ {
				gid := grad[i][d]
				sid := tsne.ystep[i][d]
				gainid := tsne.gains[i][d]
				// compute gain update
				if sign(gid) == sign(sid) {
					tsne.gains[i][d] = gainid * 0.8
				} else {
					tsne.gains[i][d] = gainid + 0.2
				}
				// compute momentum step direction
				momval := 0.8
				if tsne.iter < 250 {
					momval = 0.5
				}
				newsid := momval*sid - epsilon*tsne.gains[i][d]*grad[i][d]
				tsne.ystep[i][d] = newsid // remember the step we took
				// step!
				tsne.Solution[i][d] += newsid
				ymean[d] += tsne.Solution[i][d] // accumulate mean so that we can center later
			}
		}(i)
	}
	wg.Wait()
	// reproject Y to be zero mean
	for i := 0; i < length; i++ {
		for d := 0; d < nbDims; d++ {
			tsne.Solution[i][d] -= ymean[d] / float64(length)
		}
	}
	return cost
}

// return cost and gradient, given an arrangement
func (tsne *TSne) costGrad(Y []Point) (cost float64, grad []Point) {
	length := tsne.length
	P := tsne.probas
	pmul := 1.0
	if tsne.iter < 100 { // trick that helps with local optima
		pmul = 4.0
	}
	// compute current Q distribution, unnormalized first
	Qu := make([]float64, length*length)
	qsum := 0.0
	for i := 0; i < length-1; i++ {
		for j := i + 1; j < length; j++ {
			dsum := 0.0
			for d := 0; d < nbDims; d++ {
				dhere := Y[i][d] - Y[j][d]
				dsum += dhere * dhere
			}
			qu := 1.0 / (1.0 + dsum) // Student t-distribution
			Qu[i*length+j] = qu
			Qu[j*length+i] = qu
			qsum += 2 * qu
		}
	}
	// normalize Q distribution to sum to 1
	squareLength := length * length
	Q := make([]float64, squareLength)
	for q := range Q {
		Q[q] = math.Max(Qu[q]/qsum, 1e-100)
	}
	cost = 0.0
	grad = make([]Point, length)
	for i := 0; i < length; i++ {
		gsum := &grad[i]
		for j := 0; j < length; j++ {
			// accumulate cost (the non-constant portion at least...)
			idx := i*length + j
			cost += -P[idx] * math.Log(Q[idx])
			premult := 4 * (pmul*P[idx] - Q[idx]) * Qu[idx]
			for d := 0; d < nbDims; d++ {
				gsum[d] += premult * (Y[i][d] - Y[j][d])
			}
		}
	}

	return cost, grad
}

// Normalize makes all values from the solution in the interval [0; 1]
func (tsne *TSne) NormalizeSolution() {
	var mins [nbDims]float64
	var maxs [nbDims]float64
	for i, pt := range tsne.Solution {
		for j, val := range pt {
			if i == 0 || val < mins[j] {
				mins[j] = val
			}
			if i == 0 || val > maxs[j] {
				maxs[j] = val
			}
		}
	}
	for i, pt := range tsne.Solution {
		for j, val := range pt {
			tsne.Solution[i][j] = (val - mins[j]) / (maxs[j] - mins[j])
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func validateColumns(t *testing.T, expectedColumns []int, data []*OutputNode) {
	for idx, row := range data {
		if row.Column != expectedColumns[idx] {
			t.Fail()
			t.Logf("Id: %s, Expected column: %d, Actual column: %d", row.Id, expectedColumns[idx], row.Column)
		}
	}
}

func validatePaths(t *testing.T, expectedPaths []map[string][]Point, data []*OutputNode) {
	for nodeIdx, node := range data {
		for _, parentId := range node.Parents {
			for pathIdx, pathNode := range node.ParentsPaths[parentId] {
				if pathNode != expectedPaths[nodeIdx][parentId][pathIdx] {
					t.Fail()
					t.Logf("Id: %s, Expected path: %d, Actual path: %d", node.Id, expectedPaths[nodeIdx][parentId][pathIdx], pathNode)
				}
			}
		}
	}
}

// 1
// |
// 2
// |
// 3
func Test1(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"2"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 0, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"2": []Point{Point{0, 0, 0}, Point{0, 1, 0}},
		},
		map[string][]Point{
			"3": []Point{Point{0, 1, 0}, Point{0, 2, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// | 2
// |/
// 3
func Test2(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"3": []Point{Point{0, 0, 0}, Point{0, 2, 0}},
		},
		map[string][]Point{
			"3": []Point{Point{1, 1, 0}, Point{1, 2, 1}, Point{0, 2, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// |\
// | 2
// |/
// 3
func Test3(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"3", "2"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"3": []Point{Point{0, 0, 0}, Point{0, 2, 0}},
			"2": []Point{Point{0, 0, 0}, Point{1, 0, 2}, Point{1, 1, 0}},
		},
		map[string][]Point{
			"3": []Point{Point{1, 1, 0}, Point{1, 2, 1}, Point{0, 2, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// |\
// | 2
// 3 |
// |\|
// | |\
// | | 4
// | |/
// |/
// 5
func Test4(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"3", "2"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"5"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{"5", "4"}})
	inputNodes = append(inputNodes, InputNode{"4", []string{"5"}})
	inputNodes = append(inputNodes, InputNode{"5", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 0, 2, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"3": []Point{Point{0, 0, 0}, Point{0, 2, 0}},
			"2": []Point{Point{0, 0, 0}, Point{1, 0, 2}, Point{1, 1, 0}},
		},
		map[string][]Point{
			"5": []Point{Point{1, 1, 0}, Point{1, 4, 1}, Point{0, 4, 0}},
		},
		map[string][]Point{
			"5": []Point{Point{0, 2, 0}, Point{0, 4, 0}},
			"4": []Point{Point{0, 2, 0}, Point{2, 2, 2}, Point{2, 3, 0}},
		},
		map[string][]Point{
			"5": []Point{Point{2, 3, 0}, Point{2, 4, 1}, Point{0, 4, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}
<commit_msg>Add test<commit_after>package main

import (
	"testing"
)

func validateColumns(t *testing.T, expectedColumns []int, data []*OutputNode) {
	for idx, row := range data {
		if row.Column != expectedColumns[idx] {
			t.Fail()
			t.Logf("Id: %s, Expected column: %d, Actual column: %d", row.Id, expectedColumns[idx], row.Column)
		}
	}
}

func validatePaths(t *testing.T, expectedPaths []map[string][]Point, data []*OutputNode) {
	for nodeIdx, node := range data {
		for _, parentId := range node.Parents {
			for pathIdx, pathNode := range node.ParentsPaths[parentId] {
				if pathNode != expectedPaths[nodeIdx][parentId][pathIdx] {
					t.Fail()
					t.Logf("Id: %s, Expected path: %d, Actual path: %d", node.Id, expectedPaths[nodeIdx][parentId][pathIdx], pathNode)
				}
			}
		}
	}
}

// 1
// |
// 2
// |
// 3
func Test1(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"2"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 0, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"2": []Point{Point{0, 0, 0}, Point{0, 1, 0}},
		},
		map[string][]Point{
			"3": []Point{Point{0, 1, 0}, Point{0, 2, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// | 2
// |/
// 3
func Test2(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"3": []Point{Point{0, 0, 0}, Point{0, 2, 0}},
		},
		map[string][]Point{
			"3": []Point{Point{1, 1, 0}, Point{1, 2, 1}, Point{0, 2, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// |\
// | 2
// |/
// 3
func Test3(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"3", "2"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"3"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"3": []Point{Point{0, 0, 0}, Point{0, 2, 0}},
			"2": []Point{Point{0, 0, 0}, Point{1, 0, 2}, Point{1, 1, 0}},
		},
		map[string][]Point{
			"3": []Point{Point{1, 1, 0}, Point{1, 2, 1}, Point{0, 2, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// |\
// | 2
// 3 |
// |\|
// | |\
// | | 4
// | |/
// |/
// 5
func Test4(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"3", "2"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"5"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{"5", "4"}})
	inputNodes = append(inputNodes, InputNode{"4", []string{"5"}})
	inputNodes = append(inputNodes, InputNode{"5", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 0, 2, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"3": []Point{Point{0, 0, 0}, Point{0, 2, 0}},
			"2": []Point{Point{0, 0, 0}, Point{1, 0, 2}, Point{1, 1, 0}},
		},
		map[string][]Point{
			"5": []Point{Point{1, 1, 0}, Point{1, 4, 1}, Point{0, 4, 0}},
		},
		map[string][]Point{
			"5": []Point{Point{0, 2, 0}, Point{0, 4, 0}},
			"4": []Point{Point{0, 2, 0}, Point{2, 2, 2}, Point{2, 3, 0}},
		},
		map[string][]Point{
			"5": []Point{Point{2, 3, 0}, Point{2, 4, 1}, Point{0, 4, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}

// 1
// | 2
// | | 3
// | |/
// |/
// 4
// | 5
// |/
// 6
func Test5(t *testing.T) {
	// Initial input
	inputNodes := make([]InputNode, 0)
	inputNodes = append(inputNodes, InputNode{"1", []string{"4"}})
	inputNodes = append(inputNodes, InputNode{"2", []string{"4"}})
	inputNodes = append(inputNodes, InputNode{"3", []string{"4"}})
	inputNodes = append(inputNodes, InputNode{"4", []string{"6"}})
	inputNodes = append(inputNodes, InputNode{"5", []string{"6"}})
	inputNodes = append(inputNodes, InputNode{"6", []string{}})

	out, _ := buildTree(inputNodes)

	// Expected output
	expectedColumns := []int{0, 1, 2, 0, 1, 0}

	expectedPaths := []map[string][]Point{
		map[string][]Point{
			"4": []Point{Point{0, 0, 0}, Point{0, 3, 0}},
		},
		map[string][]Point{
			"4": []Point{Point{1, 1, 0}, Point{1, 3, 1}, Point{0, 3, 0}},
		},
		map[string][]Point{
			"4": []Point{Point{2, 2, 0}, Point{2, 3, 1}, Point{0, 3, 0}},
		},
		map[string][]Point{
			"6": []Point{Point{0, 3, 0}, Point{0, 5, 0}},
		},
		map[string][]Point{
			"6": []Point{Point{1, 4, 0}, Point{1, 5, 1}, Point{0, 5, 0}},
		},
	}

	// Validation
	validateColumns(t, expectedColumns, out)
	validatePaths(t, expectedPaths, out)
}
<|endoftext|>


<commit_before>// Copyright 2017 phcurtis fn Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package fn - includes APIs relating to function names (fn).
// Such as returning a given func name relative to its position on the
// call stack. Other APIs include returning all the func names on the
// call stack, and trace logging the entry and exiting of a func including
// its time duration.
package fn

import (
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

// Version of package fn
const Version = 0.140

// Level genealogy values for exported Lvl functions
const (
	Lme     = 0       // me
	Lpar    = Lme + 1 // parent
	Lgpar   = Lme + 2 // grandparent
	Lggpar  = Lme + 3 // great-grandparent
	Lgggpar = Lme + 4 // great-great-grandparent
)

// nameform - contains form of func name to return
type nameform uint8

// list of forms of a func name to return
const (
	nfull nameform = 0 // full name form
	nbase nameform = 1 // filepath.Base form
)

const cStkEndPfix = "<EndOfCallStack:lvlll-lvl="

// low level func getting a given 'lvl' func name
func lvlll(lvl int, nform nameform) string {
	const baselvl = 2
	pc := make([]uintptr, 10)
	runtime.Callers(baselvl+lvl, pc)
	name := runtime.FuncForPC(pc[0]).Name()
	if nform == nbase {
		name = filepath.Base(name)
	}
	if name == "" {
		name = fmt.Sprintf(cStkEndPfix+"%d>", lvl)
	}
	return name
}

// Lvl - returns the func name relative to levels back on
// caller stack it was invoked from. Use lvl=Lpar for parent func,
// lvl=Lgpar or lvl=2 for GrandParent and so on.
func Lvl(lvl int) string {
	return lvlll(lvl+Lpar, nfull)
}

// LvlBase - returns the filepath.Base form of func name relative to
// levels back on caller stack it was invoked from.
func LvlBase(lvl int) string {
	return lvlll(lvl+Lpar, nbase)
}

// Cur - returns the current func name relative to where it was invoked from.
func Cur() string {
	return lvlll(Lpar, nfull)
}

// CurBase - returns the filepath.Base form of func name relative to
// where it it was invoked from.
func CurBase() string {
	return lvlll(Lpar, nbase)
}

// LvlCStkMax -- max Level call stack depth that LvlCStk will search too.
const LvlCStkMax = 500

// LvlCStk returns func names in call stack for a given level relative
// to were it was invoked from; Typically one should use CStk instead.
// Use lvl=Lpar for parent func, lvl=LgPar for GrandParent and so on
func LvlCStk(lvl int) string {
	var name, sep string
	for i := lvl; i <= LvlCStkMax; i++ {
		cname := Lvl(i + Lpar)
		if strings.HasPrefix(cname, cStkEndPfix) {
			break
		}
		name += sep + cname
		sep = "<--" // do not change - testing is dependent on this
	}
	return name
}

// CStk - returns func names in call stack relative to where it was invoked from.
func CStk() string {
	return LvlCStk(Lpar)
}
<commit_msg>+LvlInfoNNN funcs endCallStk adjusts<commit_after>// Copyright 2017 phcurtis fn Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package fn - includes APIs relating to function names (fn).
// Such as returning a given func name relative to its position on the
// call stack. Other APIs include returning all the func names on the
// call stack, and trace logging the entry and exiting of a func including
// its time duration.
package fn

import (
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

// Version of package fn
const Version = 0.150

// Level genealogy values for exported Lvl functions
const (
	Lme     = 0       // me
	Lpar    = Lme + 1 // parent
	Lgpar   = Lme + 2 // grandparent
	Lggpar  = Lme + 3 // great-grandparent
	Lgggpar = Lme + 4 // great-great-grandparent
)

// nameform - contains form of func name to return
type nameform uint8

// list of forms of a func name to return
const (
	nfull nameform = 0 // full name form
	nbase nameform = 1 // filepath.Base form
)

//const cStkEndPfix = "<EndOfCallStack:lvlll-lvl="

// CStkEndPfix - sentinel prefix value denoting end of call stack
const CStkEndPfix = "<EndOfCallStack:"

// low level func getting a given 'lvl' func name
func lvlll(lvl int, nform nameform) string {
	const baselvl = 2
	pc := make([]uintptr, 10)
	runtime.Callers(baselvl+lvl, pc)
	name := runtime.FuncForPC(pc[0]).Name()
	if name == "" {
		name = fmt.Sprintf(CStkEndPfix+"%d>", lvl)
	} else {
		if nform == nbase {
			name = filepath.Base(name)
		}
	}
	return name
}

// Lvl - returns the func name relative to levels back on
// caller stack it was invoked from. Use lvl=Lpar for parent func,
// lvl=Lgpar or lvl=2 for GrandParent and so on.
func Lvl(lvl int) string {
	return lvlll(lvl+Lpar, nfull)
}

// flags used in funcs LvlInfoNNN.
const (
	Ifnbase = 1 << iota
	Ifnfull
	Ifileshort
	Ifilelong
	Ifuncnoparens
	Ifilenogps
	IflagsDef   = Ifnbase | Ifilenogps
	IflagsCmn   = Ifnbase | Ifilenogps
	IflagsShort = Ifnbase | Ifileshort
)

// LvlInfo - returns level info details, filename, linenum and func name
// adjusted according to flags value.
func LvlInfo(lvl int, flags int) (file string, line int, name string) {
	const baselvl = 2
	pc := make([]uintptr, 10)
	runtime.Callers(baselvl+lvl, pc)
	name = runtime.FuncForPC(pc[0]).Name()
	if name == "" {
		name = fmt.Sprintf(CStkEndPfix+"%d>", lvl)
	} else {
		if flags&Ifnbase > 0 {
			name = filepath.Base(name)
		}
		if flags&Ifuncnoparens == 0 {
			name += "()"
		}
	}
	var ok bool
	_, file, line, ok = runtime.Caller(baselvl + lvl - 1)
	if !ok {
		file = "???"
		line = 0
	}
	if flags&Ifileshort > 0 {
		file = filepath.Base(file)
	} else if flags&Ifilenogps > 0 {
		if strings.HasPrefix(file, gopathsrc) {
			file = file[len(gopathsrc):]
		}
	}
	return file, line, name
}

// LvlInfoStr - returns level one string containing info details,
// filename, linenum and func name adjusted according to flags value.
func LvlInfoStr(lvl int, flags int) string {
	file, line, name := LvlInfo(lvl+1, flags)
	return fmt.Sprintf("%s:%d:%s", file, line, name)
}

// LvlInfoCmn - returns level one string containing info details,
// filename, linenum and func name adjusted to IflagsCmn flags value.
func LvlInfoCmn(lvl int) string {
	file, line, name := LvlInfo(lvl+1, IflagsCmn)
	return fmt.Sprintf("%s:%d:%s", file, line, name)
}

// LvlInfoShort - returns level one string containing info details,
// filename, linenum and func name adjusted to IflagsShort flags value.
func LvlInfoShort(lvl int) string {
	file, line, name := LvlInfo(lvl+1, IflagsShort)
	return fmt.Sprintf("%s:%d:%s", file, line, name)
}

// LvlBase - returns the filepath.Base form of func name relative to
// levels back on caller stack it was invoked from.
func LvlBase(lvl int) string {
	return lvlll(lvl+Lpar, nbase)
}

// Cur - returns the current func name relative to where it was invoked from.
func Cur() string {
	return lvlll(Lpar, nfull)
}

// CurBase - returns the filepath.Base form of func name relative to
// where it it was invoked from.
func CurBase() string {
	return lvlll(Lpar, nbase)
}

// LvlCStkMax -- max Level call stack depth that LvlCStk will search too.
const LvlCStkMax = 500

// LvlCStk returns func names in call stack for a given level relative
// to were it was invoked from; Typically one should use CStk instead.
// Use lvl=Lpar for parent func, lvl=LgPar for GrandParent and so on
func LvlCStk(lvl int) string {
	var name, sep string
	for i := lvl; i <= LvlCStkMax; i++ {
		cname := Lvl(i + Lpar)
		//fmt.Printf("cname(%d):%s\n", i, cname)
		if strings.HasPrefix(cname, CStkEndPfix) {
			break
		}
		name += sep + cname
		sep = "<--" // do not change - testing is dependent on this
	}
	return name
}

// CStk - returns func names in call stack relative to where it was invoked from.
func CStk() string {
	return LvlCStk(Lpar)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"testing"

	"github.com/mgutz/ansi"
	"golang.org/x/tools/cover"
)

func assertDontPanic(t *testing.T, fn func(), name string) {
	defer func() {
		if recoverInfo := recover(); recoverInfo != nil {
			t.Errorf("The code panic: %s\npanic: %s", name, recoverInfo)
		}
	}()
	fn()
}

func Test_readFile(t *testing.T) {
	file, err := readFile("go-carpet_test.go")
	if err != nil {
		t.Errorf("readFile(): got error: %s", err)
	}
	if len(file) == 0 {
		t.Errorf("readFile(): file empty")
	}
	if string(file[:12]) != "package main" {
		t.Errorf("readFile(): failed read first line")
	}

	_, err = readFile("dont exists file")
	if err == nil {
		t.Errorf("File exists error:")
	}
}

func Test_getDirsWithTests(t *testing.T) {
	dirs := getDirsWithTests(".")
	if len(dirs) == 0 {
		t.Errorf("Dir list is empty")
	}
	dirs = getDirsWithTests()
	if len(dirs) == 0 {
		t.Errorf("Dir list is empty")
	}
	dirs = getDirsWithTests(".", ".")
	if len(dirs) != 1 {
		t.Errorf("The same directory failed")
	}
}

func Test_getTempFileName(t *testing.T) {
	tmpFileName, err := getTempFileName()
	if err != nil {
		t.Errorf("getTempFileName() got error")
	}
	defer os.RemoveAll(tmpFileName)

	if len(tmpFileName) == 0 {
		t.Errorf("getTempFileName() failed")
	}

	// on RO-dir
	cwd, _ := os.Getwd()
	os.Chdir("/")
	_, err = getTempFileName()
	if err == nil {
		t.Errorf("getTempFileName() not got error")
	}
	os.Chdir(cwd)
}

func Test_isSliceInString(t *testing.T) {
	testData := []struct {
		src    string
		slice  []string
		result bool
	}{
		{
			src:    "one/file.go",
			slice:  []string{"one.go", "file.go"},
			result: true,
		},
		{
			src:    "path/path/file.go",
			slice:  []string{"one.go", "path/file.go"},
			result: true,
		},
		{
			src:    "one/file.go",
			slice:  []string{"one.go", "two.go"},
			result: false,
		},
		{
			src:    "one/file.go",
			slice:  []string{},
			result: false,
		},
	}

	for i, item := range testData {
		result := isSliceInString(item.src, item.slice)
		if result != item.result {
			t.Errorf("\n%d.\nexpected: %v\nreal    :%v", i, item.result, result)
		}
	}
}

func Test_getShadeOfGreen(t *testing.T) {
	testData := []struct {
		normCover float64
		result    string
	}{
		{
			normCover: 0,
			result:    "29",
		},
		{
			normCover: 1,
			result:    "51",
		},
		{
			normCover: 0.99999,
			result:    "51",
		},
		{
			normCover: 0.5,
			result:    "40",
		},
		{
			normCover: -1,
			result:    "29",
		},
		{
			normCover: 11,
			result:    "51",
		},
		{
			normCover: 100500,
			result:    "51",
		},
	}

	for i, item := range testData {
		result := getShadeOfGreen(item.normCover)
		if result != item.result {
			t.Errorf("\n%d.\nexpected: %v\nreal    : %v", i, item.result, result)
		}
	}
}

func Test_getColorWriter(t *testing.T) {
	assertDontPanic(t, func() { getColorWriter() }, "getColorWriter()")
}

func Test_getColorHeader(t *testing.T) {
	result := getColorHeader("filename.go")
	expected := ansi.ColorCode("yellow") + "filename.go" + ansi.ColorCode("reset") + "\n" +
		ansi.ColorCode("black+h") + "~~~~~~~~~~~" + ansi.ColorCode("reset") + "\n"

	if result != expected {
		t.Errorf("1. getColorHeader() failed")
	}
}

func Test_getCoverForFile(t *testing.T) {
	fileProfile := &cover.Profile{
		FileName: "filename.go",
		Mode:     "count",
		Blocks: []cover.ProfileBlock{
			cover.ProfileBlock{
				StartLine: 2,
				StartCol:  5,
				EndLine:   2,
				EndCol:    10,
				NumStmt:   1,
				Count:     1,
			},
		},
	}
	fileContent := []byte("1 line\n123 green 456\n3 line red and other")

	coloredBytes := getCoverForFile(fileProfile, fileContent, false)
	expectOut := getColorHeader("filename.go") +
		"1 line\n" +
		"123 " + ansi.ColorCode("green") + "green" + ansi.ColorCode("reset") + " 456\n" +
		"3 line red and other\n"
	if string(coloredBytes) != expectOut {
		t.Errorf("1. getCoverForFile() failed")
	}

	// with red blocks
	fileProfile.Blocks = append(fileProfile.Blocks,
		cover.ProfileBlock{
			StartLine: 3,
			StartCol:  8,
			EndLine:   3,
			EndCol:    11,
			NumStmt:   0,
			Count:     0,
		},
	)
	coloredBytes = getCoverForFile(fileProfile, fileContent, false)
	expectOut = getColorHeader("filename.go") +
		"1 line\n" +
		"123 " + ansi.ColorCode("green") + "green" + ansi.ColorCode("reset") + " 456\n" +
		"3 line " + ansi.ColorCode("red") + "red" + ansi.ColorCode("reset") + " and other\n"
	if string(coloredBytes) != expectOut {
		t.Errorf("1. getCoverForFile() failed")
	}

	// 256 colors
	coloredBytes = getCoverForFile(fileProfile, fileContent, true)
	expectOut = getColorHeader("filename.go") +
		"1 line\n" +
		"123 " + ansi.ColorCode("48") + "green" + ansi.ColorCode("reset") + " 456\n" +
		"3 line " + ansi.ColorCode("red") + "red" + ansi.ColorCode("reset") + " and other\n"
	if string(coloredBytes) != expectOut {
		t.Errorf("1. getCoverForFile() failed")
	}
}

func Test_runGoTest(t *testing.T) {
	err := runGoTest("./not exists dir", "", true)
	if err == nil {
		t.Errorf("runGoTest() error failed")
	}
}
<commit_msg>Changes after gofmt -s<commit_after>package main

import (
	"os"
	"testing"

	"github.com/mgutz/ansi"
	"golang.org/x/tools/cover"
)

func assertDontPanic(t *testing.T, fn func(), name string) {
	defer func() {
		if recoverInfo := recover(); recoverInfo != nil {
			t.Errorf("The code panic: %s\npanic: %s", name, recoverInfo)
		}
	}()
	fn()
}

func Test_readFile(t *testing.T) {
	file, err := readFile("go-carpet_test.go")
	if err != nil {
		t.Errorf("readFile(): got error: %s", err)
	}
	if len(file) == 0 {
		t.Errorf("readFile(): file empty")
	}
	if string(file[:12]) != "package main" {
		t.Errorf("readFile(): failed read first line")
	}

	_, err = readFile("dont exists file")
	if err == nil {
		t.Errorf("File exists error:")
	}
}

func Test_getDirsWithTests(t *testing.T) {
	dirs := getDirsWithTests(".")
	if len(dirs) == 0 {
		t.Errorf("Dir list is empty")
	}
	dirs = getDirsWithTests()
	if len(dirs) == 0 {
		t.Errorf("Dir list is empty")
	}
	dirs = getDirsWithTests(".", ".")
	if len(dirs) != 1 {
		t.Errorf("The same directory failed")
	}
}

func Test_getTempFileName(t *testing.T) {
	tmpFileName, err := getTempFileName()
	if err != nil {
		t.Errorf("getTempFileName() got error")
	}
	defer os.RemoveAll(tmpFileName)

	if len(tmpFileName) == 0 {
		t.Errorf("getTempFileName() failed")
	}

	// on RO-dir
	cwd, _ := os.Getwd()
	os.Chdir("/")
	_, err = getTempFileName()
	if err == nil {
		t.Errorf("getTempFileName() not got error")
	}
	os.Chdir(cwd)
}

func Test_isSliceInString(t *testing.T) {
	testData := []struct {
		src    string
		slice  []string
		result bool
	}{
		{
			src:    "one/file.go",
			slice:  []string{"one.go", "file.go"},
			result: true,
		},
		{
			src:    "path/path/file.go",
			slice:  []string{"one.go", "path/file.go"},
			result: true,
		},
		{
			src:    "one/file.go",
			slice:  []string{"one.go", "two.go"},
			result: false,
		},
		{
			src:    "one/file.go",
			slice:  []string{},
			result: false,
		},
	}

	for i, item := range testData {
		result := isSliceInString(item.src, item.slice)
		if result != item.result {
			t.Errorf("\n%d.\nexpected: %v\nreal    :%v", i, item.result, result)
		}
	}
}

func Test_getShadeOfGreen(t *testing.T) {
	testData := []struct {
		normCover float64
		result    string
	}{
		{
			normCover: 0,
			result:    "29",
		},
		{
			normCover: 1,
			result:    "51",
		},
		{
			normCover: 0.99999,
			result:    "51",
		},
		{
			normCover: 0.5,
			result:    "40",
		},
		{
			normCover: -1,
			result:    "29",
		},
		{
			normCover: 11,
			result:    "51",
		},
		{
			normCover: 100500,
			result:    "51",
		},
	}

	for i, item := range testData {
		result := getShadeOfGreen(item.normCover)
		if result != item.result {
			t.Errorf("\n%d.\nexpected: %v\nreal    : %v", i, item.result, result)
		}
	}
}

func Test_getColorWriter(t *testing.T) {
	assertDontPanic(t, func() { getColorWriter() }, "getColorWriter()")
}

func Test_getColorHeader(t *testing.T) {
	result := getColorHeader("filename.go")
	expected := ansi.ColorCode("yellow") + "filename.go" + ansi.ColorCode("reset") + "\n" +
		ansi.ColorCode("black+h") + "~~~~~~~~~~~" + ansi.ColorCode("reset") + "\n"

	if result != expected {
		t.Errorf("1. getColorHeader() failed")
	}
}

func Test_getCoverForFile(t *testing.T) {
	fileProfile := &cover.Profile{
		FileName: "filename.go",
		Mode:     "count",
		Blocks: []cover.ProfileBlock{
			{
				StartLine: 2,
				StartCol:  5,
				EndLine:   2,
				EndCol:    10,
				NumStmt:   1,
				Count:     1,
			},
		},
	}
	fileContent := []byte("1 line\n123 green 456\n3 line red and other")

	coloredBytes := getCoverForFile(fileProfile, fileContent, false)
	expectOut := getColorHeader("filename.go") +
		"1 line\n" +
		"123 " + ansi.ColorCode("green") + "green" + ansi.ColorCode("reset") + " 456\n" +
		"3 line red and other\n"
	if string(coloredBytes) != expectOut {
		t.Errorf("1. getCoverForFile() failed")
	}

	// with red blocks
	fileProfile.Blocks = append(fileProfile.Blocks,
		cover.ProfileBlock{
			StartLine: 3,
			StartCol:  8,
			EndLine:   3,
			EndCol:    11,
			NumStmt:   0,
			Count:     0,
		},
	)
	coloredBytes = getCoverForFile(fileProfile, fileContent, false)
	expectOut = getColorHeader("filename.go") +
		"1 line\n" +
		"123 " + ansi.ColorCode("green") + "green" + ansi.ColorCode("reset") + " 456\n" +
		"3 line " + ansi.ColorCode("red") + "red" + ansi.ColorCode("reset") + " and other\n"
	if string(coloredBytes) != expectOut {
		t.Errorf("1. getCoverForFile() failed")
	}

	// 256 colors
	coloredBytes = getCoverForFile(fileProfile, fileContent, true)
	expectOut = getColorHeader("filename.go") +
		"1 line\n" +
		"123 " + ansi.ColorCode("48") + "green" + ansi.ColorCode("reset") + " 456\n" +
		"3 line " + ansi.ColorCode("red") + "red" + ansi.ColorCode("reset") + " and other\n"
	if string(coloredBytes) != expectOut {
		t.Errorf("1. getCoverForFile() failed")
	}
}

func Test_runGoTest(t *testing.T) {
	err := runGoTest("./not exists dir", "", true)
	if err == nil {
		t.Errorf("runGoTest() error failed")
	}
}
<|endoftext|>


<commit_before>package main

import (
	//remove the following line to not have your deployment tracker
	"github.com/IBM-Bluemix/cf_deployment_tracker_client_go"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
)

func main() {
	//remove the following line to not have your deployment tracker
	cf_deployment_tracker.Track()
	router := gin.Default()
	router.LoadHTMLGlob("templates/*")

	router.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "Main website",
		})
	})

	router.GET("/hi", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "hi",
		})
	})

	port := os.Getenv("VCAP_APP_PORT")
	if port == "" {
		port = "8080"
	}
	router.Run(":" + port)
}
<commit_msg>Update PORT env var<commit_after>package main

import (
	//remove the following line to not have your deployment tracker
	"github.com/IBM-Bluemix/cf_deployment_tracker_client_go"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
)

func main() {
	//remove the following line to not have your deployment tracker
	cf_deployment_tracker.Track()
	router := gin.Default()
	router.LoadHTMLGlob("templates/*")

	router.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "Main website",
		})
	})

	router.GET("/hi", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "hi",
		})
	})

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	router.Run(":" + port)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type opts struct {
	Flakes      int
	Fails       int
	Prefix      string
	S3Bucket    string
	DirBasename string
	BuildID     string
	Branch      string
	Preserve    bool
	BuildURL    string
	NoCompile   bool
	TestBinary  string
	Timeout     string
}

func logError(f string, args ...interface{}) {
	s := fmt.Sprintf(f, args...)
	if s[len(s)-1] != '\n' {
		s += "\n"
	}
	fmt.Fprintf(os.Stderr, "%s", s)
}

type runner struct {
	opts   opts
	flakes int
	fails  int
	tests  []string
}

func convertPrefix(p string) string {
	s := fmt.Sprintf("%c", os.PathSeparator)
	return strings.ReplaceAll(p, s, "_")
}

func (r *runner) parseArgs() (err error) {
	flag.IntVar(&r.opts.Flakes, "flakes", 3, "number of allowed flakes")
	flag.IntVar(&r.opts.Fails, "fails", -1, "number of fails allowed before quitting")
	var prfx string
	flag.StringVar(&prfx, "prefix", "", "test set prefix")
	flag.StringVar(&r.opts.S3Bucket, "s3bucket", "", "AWS S3 bucket to write failures to")
	flag.StringVar(&r.opts.BuildID, "build-id", "", "build ID of the current build")
	flag.StringVar(&r.opts.Branch, "branch", "", "the branch of the current build")
	flag.BoolVar(&r.opts.Preserve, "preserve", false, "preserve test binary after done")
	flag.StringVar(&r.opts.BuildURL, "build-url", "", "URL for this build (in CI mainly)")
	flag.BoolVar(&r.opts.NoCompile, "no-compile", false, "specify flag if you've pre-compiled the test")
	flag.StringVar(&r.opts.TestBinary, "test-binary", "", "specify the test binary to run")
	flag.StringVar(&r.opts.Timeout, "timeout", "60s", "timeout (in seconds) for any one individual test")
	flag.Parse()
	r.opts.Prefix = convertPrefix(prfx)
	var d string
	d, err = os.Getwd()
	if err != nil {
		return err
	}
	r.opts.DirBasename = filepath.Base(d)
	return nil
}

func (r *runner) compile() error {
	if r.opts.NoCompile {
		return nil
	}
	fmt.Printf("CMPL: %s\n", r.testerName())
	cmd := exec.Command("go", "test", "-c")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func filter(v []string) []string {
	var ret []string
	for _, s := range v {
		if s != "" {
			ret = append(ret, s)
		}
	}
	return ret
}

func (r *runner) testerName() string {
	if r.opts.TestBinary != "" {
		return r.opts.TestBinary
	}
	return fmt.Sprintf(".%c%s.test", os.PathSeparator, r.opts.DirBasename)
}

func (r *runner) listTests() error {
	cmd := exec.Command(r.testerName(), "-test.list", ".")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return err
	}
	r.tests = filter(strings.Split(out.String(), "\n"))
	return nil
}

func (r *runner) flushTestLogs(test string, log bytes.Buffer) (string, error) {
	buildID := strings.ReplaceAll(r.opts.BuildID, "-", "_")
	branch := strings.ReplaceAll(r.opts.Branch, "-", "_")
	logName := fmt.Sprintf("citogo-%s-%s-%s-%s", branch, buildID, r.opts.Prefix, test)
	if r.opts.S3Bucket != "" {
		return r.flushLogsToS3(logName, log)
	}
	return r.flushTestLogsToTemp(logName, log)
}

func (r *runner) flushLogsToS3(logName string, log bytes.Buffer) (string, error) {
	return s3put(&log, r.opts.S3Bucket, logName)
}

func (r *runner) flushTestLogsToTemp(logName string, log bytes.Buffer) (string, error) {
	tmpfile, err := ioutil.TempFile("", fmt.Sprintf("%s-", logName))
	if err != nil {
		return "", err
	}
	_, err = tmpfile.Write(log.Bytes())
	if err != nil {
		return "", err
	}
	err = tmpfile.Close()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("see log: %s", tmpfile.Name()), nil
}

func (r *runner) reportFlake(test string, logs string) {
	hook := os.Getenv("CITOGO_FLAKE_WEBHOOK")
	if hook == "" {
		return
	}

	r.doHook(hook, test, logs, "❄")
}

func (r *runner) doHook(hook string, test string, logs string, emoji string) {
	hook += url.QueryEscape(fmt.Sprintf("%s _client_ %s-%s %s *%s* %s [%s]", emoji, r.opts.Branch, r.opts.BuildID, r.opts.Prefix, test, logs, r.opts.BuildURL))
	_, err := http.Get(hook)
	if err != nil {
		logError("error reporting flake: %s", err.Error())
	}
}

type outcome string

const (
	success outcome = "success"
	flake   outcome = "flake"
	fail    outcome = "fail"
)

func (o outcome) abbrv() string {
	switch o {
	case success:
		return "PASS"
	case flake:
		return "FLK?"
	case fail:
		return "FAIL"
	default:
		return "????"
	}
}

func (r *runner) reportTestOutcome(outcome outcome, test string, where string) {
	fmt.Printf("%s: %s", outcome.abbrv(), test)
	if where != "" {
		fmt.Printf(" %s", where)
	}
	fmt.Printf("\n")
	if outcome == success {
		return
	}

	hook := os.Getenv("CITOGO_MASTER_FAIL_WEBHOOK")
	if hook == "" || r.opts.Branch != "master" {
		return
	}
	r.doHook(hook, test, where, "🐳")
}

func (r *runner) runTest(test string) error {
	canRerun := r.flakes < r.opts.Flakes
	logs, err := r.runTestOnce(test, canRerun, false)
	if err == errTestFailed && canRerun {
		_, err = r.runTestOnce(test, false, true)
		if err == nil {
			r.reportFlake(test, logs)
			r.flakes++
		}
	}
	return err
}

var errTestFailed = errors.New("test failed")

func (r *runner) runTestOnce(test string, canRerun bool, isRerun bool) (string, error) {
	cmd := exec.Command(r.testerName(), "-test.run", "^"+test+"$", "-test.timeout", r.opts.Timeout)
	var combined bytes.Buffer
	if isRerun {
		cmd.Env = append(os.Environ(), "CITOGO_FLAKE_RERUN=1")
	}
	cmd.Stdout = &combined
	cmd.Stderr = &combined
	err := cmd.Run()
	if err != nil {
		err = errTestFailed
	}
	var where string
	var status outcome
	if err != nil {
		var flushErr error
		where, flushErr = r.flushTestLogs(test, combined)
		if flushErr != nil {
			return "", flushErr
		}
		if canRerun {
			status = flake
		} else {
			status = fail
		}
	} else {
		status = success
	}
	r.reportTestOutcome(status, test, where)
	return where, err
}

func (r *runner) runTestFixError(t string) error {
	err := r.runTest(t)
	if err == nil {
		return nil
	}
	if err != errTestFailed {
		return err
	}
	r.fails++
	if r.opts.Fails < 0 {
		// We have an infinite fail budget, so keep plowing through
		// failed tests. This test run is still going to fail.
		return nil
	}
	if r.opts.Fails >= r.fails {
		// We've failed less than our budget, so we can still keep going.
		// This test run is still going to fail.
		return nil
	}
	// We ate up our fail budget.
	return err
}

func (r *runner) runTests() error {
	for _, f := range r.tests {
		err := r.runTestFixError(f)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *runner) cleanup() {
	if r.opts.Preserve || r.opts.NoCompile {
		return
	}
	n := r.testerName()
	err := os.Remove(n)
	if err != nil {
		logError("could not remove %s: %s", n, err.Error())
	}
}

func (r *runner) debugStartup() {
	dir, _ := os.Getwd()
	fmt.Printf("WDIR: %s\n", dir)
}

func (r *runner) run() error {
	start := time.Now()
	err := r.parseArgs()
	if err != nil {
		return err
	}
	r.debugStartup()
	err = r.compile()
	if err != nil {
		return err
	}
	err = r.listTests()
	if err != nil {
		return err
	}
	err = r.runTests()
	r.cleanup()
	end := time.Now()
	diff := end.Sub(start)
	fmt.Printf("DONE: in %s\n", diff)
	if err != nil {
		return err
	}
	if r.fails > 0 {
		return fmt.Errorf("RED!: %d total tests failed", r.fails)
	}
	return nil
}

func main2() error {
	runner := runner{}
	return runner.run()
}

func main() {
	err := main2()
	if err != nil {
		logError(err.Error())
		fmt.Printf("EXIT: 2\n")
		os.Exit(2)
	}
	fmt.Printf("EXIT: 0\n")
	os.Exit(0)
}
<commit_msg>citogo: fix kbfs/dokan log path bug<commit_after>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type opts struct {
	Flakes      int
	Fails       int
	Prefix      string
	S3Bucket    string
	DirBasename string
	BuildID     string
	Branch      string
	Preserve    bool
	BuildURL    string
	NoCompile   bool
	TestBinary  string
	Timeout     string
}

func logError(f string, args ...interface{}) {
	s := fmt.Sprintf(f, args...)
	if s[len(s)-1] != '\n' {
		s += "\n"
	}
	fmt.Fprintf(os.Stderr, "%s", s)
}

type runner struct {
	opts   opts
	flakes int
	fails  int
	tests  []string
}

func convertBreakingChars(s string) string {
	// replace either the unix or the DOS directory marker
	// with an underscore, so as not to break the directory
	// structure of where we are storing the log
	s = strings.ReplaceAll(s, "/", "_")
	s = strings.ReplaceAll(s, "\\", "_")
	s = strings.ReplaceAll(s, "-", "_")
	return s
}

func (r *runner) parseArgs() (err error) {
	flag.IntVar(&r.opts.Flakes, "flakes", 3, "number of allowed flakes")
	flag.IntVar(&r.opts.Fails, "fails", -1, "number of fails allowed before quitting")
	flag.StringVar(&r.opts.Prefix, "prefix", "", "test set prefix")
	flag.StringVar(&r.opts.S3Bucket, "s3bucket", "", "AWS S3 bucket to write failures to")
	flag.StringVar(&r.opts.BuildID, "build-id", "", "build ID of the current build")
	flag.StringVar(&r.opts.Branch, "branch", "", "the branch of the current build")
	flag.BoolVar(&r.opts.Preserve, "preserve", false, "preserve test binary after done")
	flag.StringVar(&r.opts.BuildURL, "build-url", "", "URL for this build (in CI mainly)")
	flag.BoolVar(&r.opts.NoCompile, "no-compile", false, "specify flag if you've pre-compiled the test")
	flag.StringVar(&r.opts.TestBinary, "test-binary", "", "specify the test binary to run")
	flag.StringVar(&r.opts.Timeout, "timeout", "60s", "timeout (in seconds) for any one individual test")
	flag.Parse()
	var d string
	d, err = os.Getwd()
	if err != nil {
		return err
	}
	r.opts.DirBasename = filepath.Base(d)
	return nil
}

func (r *runner) compile() error {
	if r.opts.NoCompile {
		return nil
	}
	fmt.Printf("CMPL: %s\n", r.testerName())
	cmd := exec.Command("go", "test", "-c")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func filter(v []string) []string {
	var ret []string
	for _, s := range v {
		if s != "" {
			ret = append(ret, s)
		}
	}
	return ret
}

func (r *runner) testerName() string {
	if r.opts.TestBinary != "" {
		return r.opts.TestBinary
	}
	return fmt.Sprintf(".%c%s.test", os.PathSeparator, r.opts.DirBasename)
}

func (r *runner) listTests() error {
	cmd := exec.Command(r.testerName(), "-test.list", ".")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return err
	}
	r.tests = filter(strings.Split(out.String(), "\n"))
	return nil
}

func (r *runner) flushTestLogs(test string, log bytes.Buffer) (string, error) {
	logName := fmt.Sprintf("citogo-%s-%s-%s-%s", convertBreakingChars(r.opts.Branch),
		convertBreakingChars(r.opts.BuildID), convertBreakingChars(r.opts.Prefix), test)
	if r.opts.S3Bucket != "" {
		return r.flushLogsToS3(logName, log)
	}
	return r.flushTestLogsToTemp(logName, log)
}

func (r *runner) flushLogsToS3(logName string, log bytes.Buffer) (string, error) {
	return s3put(&log, r.opts.S3Bucket, logName)
}

func (r *runner) flushTestLogsToTemp(logName string, log bytes.Buffer) (string, error) {
	tmpfile, err := ioutil.TempFile("", fmt.Sprintf("%s-", logName))
	if err != nil {
		return "", err
	}
	_, err = tmpfile.Write(log.Bytes())
	if err != nil {
		return "", err
	}
	err = tmpfile.Close()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("see log: %s", tmpfile.Name()), nil
}

func (r *runner) reportFlake(test string, logs string) {
	hook := os.Getenv("CITOGO_FLAKE_WEBHOOK")
	if hook == "" {
		return
	}

	r.doHook(hook, test, logs, "❄")
}

func (r *runner) doHook(hook string, test string, logs string, emoji string) {
	hook += url.QueryEscape(fmt.Sprintf("%s _client_ %s-%s %s *%s* %s [%s]", emoji, r.opts.Branch, r.opts.BuildID, r.opts.Prefix, test, logs, r.opts.BuildURL))
	_, err := http.Get(hook)
	if err != nil {
		logError("error reporting flake: %s", err.Error())
	}
}

type outcome string

const (
	success outcome = "success"
	flake   outcome = "flake"
	fail    outcome = "fail"
)

func (o outcome) abbrv() string {
	switch o {
	case success:
		return "PASS"
	case flake:
		return "FLK?"
	case fail:
		return "FAIL"
	default:
		return "????"
	}
}

func (r *runner) reportTestOutcome(outcome outcome, test string, where string) {
	fmt.Printf("%s: %s", outcome.abbrv(), test)
	if where != "" {
		fmt.Printf(" %s", where)
	}
	fmt.Printf("\n")
	if outcome == success {
		return
	}

	hook := os.Getenv("CITOGO_MASTER_FAIL_WEBHOOK")
	if hook == "" || r.opts.Branch != "master" {
		return
	}
	r.doHook(hook, test, where, "🐳")
}

func (r *runner) runTest(test string) error {
	canRerun := r.flakes < r.opts.Flakes
	logs, err := r.runTestOnce(test, canRerun, false)
	if err == errTestFailed && canRerun {
		_, err = r.runTestOnce(test, false, true)
		if err == nil {
			r.reportFlake(test, logs)
			r.flakes++
		}
	}
	return err
}

var errTestFailed = errors.New("test failed")

func (r *runner) runTestOnce(test string, canRerun bool, isRerun bool) (string, error) {
	cmd := exec.Command(r.testerName(), "-test.run", "^"+test+"$", "-test.timeout", r.opts.Timeout)
	var combined bytes.Buffer
	if isRerun {
		cmd.Env = append(os.Environ(), "CITOGO_FLAKE_RERUN=1")
	}
	cmd.Stdout = &combined
	cmd.Stderr = &combined
	err := cmd.Run()
	if err != nil {
		err = errTestFailed
	}
	var where string
	var status outcome
	if err != nil {
		var flushErr error
		where, flushErr = r.flushTestLogs(test, combined)
		if flushErr != nil {
			return "", flushErr
		}
		if canRerun {
			status = flake
		} else {
			status = fail
		}
	} else {
		status = success
	}
	r.reportTestOutcome(status, test, where)
	return where, err
}

func (r *runner) runTestFixError(t string) error {
	err := r.runTest(t)
	if err == nil {
		return nil
	}
	if err != errTestFailed {
		return err
	}
	r.fails++
	if r.opts.Fails < 0 {
		// We have an infinite fail budget, so keep plowing through
		// failed tests. This test run is still going to fail.
		return nil
	}
	if r.opts.Fails >= r.fails {
		// We've failed less than our budget, so we can still keep going.
		// This test run is still going to fail.
		return nil
	}
	// We ate up our fail budget.
	return err
}

func (r *runner) runTests() error {
	for _, f := range r.tests {
		err := r.runTestFixError(f)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *runner) cleanup() {
	if r.opts.Preserve || r.opts.NoCompile {
		return
	}
	n := r.testerName()
	err := os.Remove(n)
	if err != nil {
		logError("could not remove %s: %s", n, err.Error())
	}
}

func (r *runner) debugStartup() {
	dir, _ := os.Getwd()
	fmt.Printf("WDIR: %s\n", dir)
}

func (r *runner) run() error {
	start := time.Now()
	err := r.parseArgs()
	if err != nil {
		return err
	}
	r.debugStartup()
	err = r.compile()
	if err != nil {
		return err
	}
	err = r.listTests()
	if err != nil {
		return err
	}
	err = r.runTests()
	r.cleanup()
	end := time.Now()
	diff := end.Sub(start)
	fmt.Printf("DONE: in %s\n", diff)
	if err != nil {
		return err
	}
	if r.fails > 0 {
		return fmt.Errorf("RED!: %d total tests failed", r.fails)
	}
	return nil
}

func main2() error {
	runner := runner{}
	return runner.run()
}

func main() {
	err := main2()
	if err != nil {
		logError(err.Error())
		fmt.Printf("EXIT: 2\n")
		os.Exit(2)
	}
	fmt.Printf("EXIT: 0\n")
	os.Exit(0)
}
<|endoftext|>


<commit_before>package clock

import (
	"time"
)

// Clock defines a type for keeping time.
type Clock struct {
	time time.Time
}

// New is a constructor to create instances of Clock.
func New(hour, min int) Clock {
	return Clock{time: time.Date(0, 0, 0, hour, min, 0, 0, time.UTC)}
}

func (c Clock) String() string {
	return c.time.Format("15:04")
}

// Add moves the clock forward by the provided number of minutes.
func (c Clock) Add(minutes int) Clock {
	// TODO
	return c
}

// Subtract moves the clock forward by the provided number of minutes.
func (c Clock) Subtract(minutes int) Clock {
	// TODO
	return c
}
<commit_msg>Implement add and subtract<commit_after>package clock

import (
	"fmt"
	"time"
)

// Clock defines a type for keeping time.
type Clock struct {
	time time.Time
}

// New is a constructor to create instances of Clock.
func New(hour, min int) Clock {
	return Clock{time: time.Date(0, 0, 0, hour, min, 0, 0, time.UTC)}
}

func (c Clock) String() string {
	return c.time.Format("15:04")
}

// Add moves the clock forward by the provided number of minutes.
func (c Clock) Add(minutes int) Clock {
	d, _ := time.ParseDuration(fmt.Sprintf("%dm", minutes)) // WARNING: Ignoring errors
	return Clock{time: c.time.Add(d)}
}

// Subtract moves the clock forward by the provided number of minutes.
func (c Clock) Subtract(minutes int) Clock {
	d, _ := time.ParseDuration(fmt.Sprintf("%dm", minutes)) // WARNING: Ignoring errors
	return Clock{time: c.time.Add(-d)}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"time"

	"github.com/freneticmonkey/migrate/go/config"
	"github.com/freneticmonkey/migrate/go/exec"
	"github.com/freneticmonkey/migrate/go/id"
	"github.com/freneticmonkey/migrate/go/migration"
	"github.com/freneticmonkey/migrate/go/mysql"
	"github.com/freneticmonkey/migrate/go/table"
	"github.com/freneticmonkey/migrate/go/util"
	"github.com/freneticmonkey/migrate/go/yaml"
	"github.com/urfave/cli"
)

// TODO: Need to reinsert metadata when inserting new tables.  This is not being done at all presently

// GetSandboxCommand Configure the sandbox command
func GetSandboxCommand() (setup cli.Command) {
	setup = cli.Command{
		Name:  "sandbox",
		Usage: "Recreate the target database from the YAML Schema and insert the metadata",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "recreate",
				Usage: "Recreate the sandbox schema.",
			},
			cli.BoolFlag{
				Name:  "migrate",
				Usage: "Migrate sandbox database to current schema state",
			},
			cli.BoolFlag{
				Name:  "dryrun",
				Usage: "Perform a dryrun of the migration.",
			},
			cli.BoolFlag{
				Name:  "force",
				Usage: "Extremely Dangerous!!! Force the recreation of schema.",
			},
		},
		Action: func(ctx *cli.Context) (err error) {

			var conf config.Config

			// Parse global flags
			parseGlobalFlags(ctx)

			// Setup the management database and configuration settings
			conf, err = configureManagement()

			if err != nil {
				return cli.NewExitError("Configuration Load failed.", 1)
			}

			// Process command line flags
			return sandboxProcessFlags(conf, ctx.Bool("recreate"), ctx.Bool("migrate"), ctx.Bool("dryrun"), ctx.Bool("force"))
		},
	}
	return setup
}

// sandboxProcessFlags Setup the Sandbox operation
func sandboxProcessFlags(conf config.Config, recreate, migrate, dryrun, force bool) (err error) {
	var successmsg string

	const YES, NO = "yes", "no"
	action := NO

	if migrate || recreate {

		if conf.Project.DB.Environment != "SANDBOX" && !force {
			return cli.NewExitError("Configured database isn't SANDBOX. Halting. If required use the force option.", 1)
		}

		if migrate {
			// If performing a migration

			successmsg, err = sandboxAction(conf, dryrun, false, "Sandbox Migration")
			if util.ErrorCheck(err) {
				return cli.NewExitError(err.Error(), 1)
			}
			return cli.NewExitError(successmsg, 0)

		}

		// If recreating the sandbox database from scratch

		// If a dryrun, or being forced, don't prompt
		if dryrun || force {
			action = YES
		}

		if action == "" {
			action, err = util.SelectAction("Are you sure you want to reset your sandbox?", []string{YES, NO})
			if util.ErrorCheck(err) {
				return cli.NewExitError("There was a problem confirming the action.", 1)
			}
		}

		switch action {
		case YES:
			{
				successmsg, err = sandboxAction(conf, dryrun, true, "Sandbox Recreation")
				if util.ErrorCheck(err) {
					return cli.NewExitError(err.Error(), 1)
				}
				return cli.NewExitError(successmsg, 0)
			}
		}
		return cli.NewExitError("Sandbox Recreation cancelled.", 0)

	}
	return cli.NewExitError("No known parameters supplied.  Please refer to help for sandbox options.", 1)
}

func sandboxAction(conf config.Config, dryrun bool, recreate bool, actionTitle string) (successmsg string, err error) {
	util.LogInfo(actionTitle)

	// Kick off a migration to recreate the db

	// Check that a local schema exists
	forwardOps, backwardOps, err := diffSchema(conf, actionTitle, recreate)

	if util.ErrorCheck(err) {
		return successmsg, err
	}

	// Create a local migration
	m, err := createMigration(conf, actionTitle, dryrun, forwardOps, backwardOps)

	if util.ErrorCheck(err) {
		return successmsg, err
	}

	// If a clean migration
	if recreate {
		// Recreate the Sandbox Database
		recreateProjectDatabase(conf, dryrun)
	}

	// Apply the migration to the sandbox
	err = migrateSandbox(actionTitle, dryrun, &m)
	if util.ErrorCheck(err) {
		return successmsg, err
	}
	dr := ""
	if dryrun {
		dr = "(DRYRUN)"
	}
	successmsg = fmt.Sprintf("%s %s: Migration successfully with ID: %d", dr, actionTitle, m.MID)
	util.LogInfo(successmsg)

	return successmsg, err
}

func migrateSandbox(actionTitle string, dryrun bool, m *migration.Migration) (err error) {
	util.LogInfof(formatMessage(dryrun, actionTitle, "Applying Schema"))
	exec.Exec(exec.Options{
		Dryrun:           dryrun,
		Force:            true,
		Rollback:         true,
		PTODisabled:      true,
		AllowDestructive: true,
		Sandbox:          true,
		Migration:        m,
	})
	if util.ErrorCheck(err) {
		err = fmt.Errorf("%s: Execute failed. Unable to execute sandbox Migration with ID: [%d]", actionTitle, m.MID)
	}

	return err
}

func diffSchema(conf config.Config, actionTitle string, recreate bool) (forwardOps mysql.SQLOperations, backwardOps mysql.SQLOperations, err error) {

	var forwardDiff table.Differences
	var backwardDiff table.Differences
	// Read the YAML schema
	err = yaml.ReadTables(conf.Project.LocalSchema.Path)
	if util.ErrorCheck(err) {
		err = fmt.Errorf("%s failed. Unable to read YAML Tables", actionTitle)
	}

	// If schema was found
	if len(yaml.Schema) > 0 {
		// Validate the YAML Schema
		_, err = id.ValidateSchema(yaml.Schema, "YAML Schema")
		if util.ErrorCheck(err) {
			err = fmt.Errorf("%s failed. YAML Validation Errors Detected", actionTitle)
		}

		// Read the MySQL tables from the target database
		err = mysql.ReadTables()
		if util.ErrorCheck(err) {
			err = fmt.Errorf("%s failed. Unable to read MySQL Tables", actionTitle)
		}

		// Don't bother validating the database if we're going to wipe it.
		if !recreate {
			// Validate the MySQL Schema
			_, err = id.ValidateSchema(mysql.Schema, "Target Database Schema")
			if util.ErrorCheck(err) {
				err = fmt.Errorf("%s failed. Target Database Validation Errors Detected", actionTitle)
			}
		}

		// Generate Diffs
		forwardDiff, err = table.DiffTables(yaml.Schema, mysql.Schema)
		if util.ErrorCheckf(err, "Diff Failed while generating forward migration") {
			return forwardOps, backwardOps, err
		}
		forwardOps = mysql.GenerateAlters(forwardDiff)

		backwardDiff, err = table.DiffTables(mysql.Schema, yaml.Schema)
		if util.ErrorCheckf(err, "Diff Failed while generating backward migration") {
			return forwardOps, backwardOps, err
		}
		backwardOps = mysql.GenerateAlters(backwardDiff)
	}

	return forwardOps, backwardOps, err
}

func createMigration(conf config.Config, actionTitle string, dryrun bool, forwardOps mysql.SQLOperations, backwardOps mysql.SQLOperations) (m migration.Migration, err error) {
	if !dryrun {
		util.LogInfo(formatMessage(dryrun, "Sandbox Create Migration", "Inserting new Migration into the DB"))
		// Create a temporary migration.  If there a way we can avoid this?
		m, err = migration.New(migration.Param{
			Project: conf.Project.Name,
			Version: conf.Project.Schema.Version,
			// Use the current state of the local Git repo (Don't do a git checkout )
			// Migration Database doesn't need to have any git info in it because this feature is for testing
			// migrations without having checked them in
			Timestamp:   time.Now().UTC().Format(mysql.TimeFormat),
			Description: actionTitle,
			Forwards:    forwardOps,
			Backwards:   backwardOps,
			Sandbox:     true,
		})

		if util.ErrorCheck(err) {
			err = fmt.Errorf("%s failed. Unable to create new Migration in the management database", actionTitle)
		} else {
			util.LogInfof("%s: Created Migration with ID: %d", actionTitle, m.MID)
		}

	} else {
		util.LogInfof("(DRYRUN) Skipping Creating Migration")
	}

	return m, err
}

func recreateProjectDatabase(conf config.Config, dryrun bool) (err error) {
	var output string

	dropCommand := fmt.Sprintf("DROP DATABASE `%s`", conf.Project.DB.Database)
	createCommand := fmt.Sprintf("CREATE DATABASE `%s`", conf.Project.DB.Database)

	util.LogInfo(formatMessage(dryrun, "Sandbox Recreation", "Recreating Database"))
	if !dryrun {
		output, err = exec.ExecuteSQL(dropCommand, false)
		if util.ErrorCheckf(err, "Problem dropping DATABASE for Project: [%s] SQL: [%s] Output: [%s]", conf.Project.Name, dropCommand, output) {
			return cli.NewExitError("Sandbox Recreation failed. Couldn't DROP Project Database", 1)
		}

		output, err = exec.ExecuteSQL(createCommand, false)
		if util.ErrorCheckf(err, "Problem creating DATABASE for Project: [%s] SQL: [%s] Output: [%s]", conf.Project.Name, createCommand, output) {
			return cli.NewExitError("Sandbox Recreation failed. Couldn't Create Project Database", 1)
		}

		// Force a Reconnect to the database because the DB was just recreated
		exec.ConnectProjectDB(true)

	} else {
		util.LogInfof("(DRYRUN) Exec SQL: %s", dropCommand)
		util.LogInfof("(DRYRUN) Exec SQL: %s", createCommand)
	}

	return err
}

func formatMessage(dryrun bool, context string, message string, info ...interface{}) string {
	message = fmt.Sprintf("%s: %s", context, fmt.Sprintf(message, info...))
	if dryrun {
		message = "(DRYRUN) " + message
	}
	return message
}
<commit_msg>Added some extra context while developing unit tests.<commit_after>package cmd

import (
	"fmt"
	"time"

	"github.com/freneticmonkey/migrate/go/config"
	"github.com/freneticmonkey/migrate/go/exec"
	"github.com/freneticmonkey/migrate/go/id"
	"github.com/freneticmonkey/migrate/go/migration"
	"github.com/freneticmonkey/migrate/go/mysql"
	"github.com/freneticmonkey/migrate/go/table"
	"github.com/freneticmonkey/migrate/go/util"
	"github.com/freneticmonkey/migrate/go/yaml"
	"github.com/urfave/cli"
)

// TODO: Need to reinsert metadata when inserting new tables.  This is not being done at all presently

// GetSandboxCommand Configure the sandbox command
func GetSandboxCommand() (setup cli.Command) {
	setup = cli.Command{
		Name:  "sandbox",
		Usage: "Recreate the target database from the YAML Schema and insert the metadata",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "recreate",
				Usage: "Recreate the sandbox schema.",
			},
			cli.BoolFlag{
				Name:  "migrate",
				Usage: "Migrate sandbox database to current schema state",
			},
			cli.BoolFlag{
				Name:  "dryrun",
				Usage: "Perform a dryrun of the migration.",
			},
			cli.BoolFlag{
				Name:  "force",
				Usage: "Extremely Dangerous!!! Force the recreation of schema.",
			},
		},
		Action: func(ctx *cli.Context) (err error) {

			var conf config.Config

			// Parse global flags
			parseGlobalFlags(ctx)

			// Setup the management database and configuration settings
			conf, err = configureManagement()

			if err != nil {
				return cli.NewExitError("Configuration Load failed.", 1)
			}

			// Process command line flags
			return sandboxProcessFlags(conf, ctx.Bool("recreate"), ctx.Bool("migrate"), ctx.Bool("dryrun"), ctx.Bool("force"))
		},
	}
	return setup
}

// sandboxProcessFlags Setup the Sandbox operation
func sandboxProcessFlags(conf config.Config, recreate, migrate, dryrun, force bool) (err error) {
	var successmsg string

	const YES, NO = "yes", "no"
	action := NO

	if migrate || recreate {

		if conf.Project.DB.Environment != "SANDBOX" && !force {
			return cli.NewExitError("Configured database isn't SANDBOX. Halting. If required use the force option.", 1)
		}

		if migrate {
			// If performing a migration

			successmsg, err = sandboxAction(conf, dryrun, false, "Sandbox Migration")
			if util.ErrorCheck(err) {
				return cli.NewExitError(err.Error(), 1)
			}
			return cli.NewExitError(successmsg, 0)

		}

		// If recreating the sandbox database from scratch

		// If a dryrun, or being forced, don't prompt
		if dryrun || force {
			action = YES
		}

		if action == "" {
			action, err = util.SelectAction("Are you sure you want to reset your sandbox?", []string{YES, NO})
			if util.ErrorCheck(err) {
				return cli.NewExitError("There was a problem confirming the action.", 1)
			}
		}

		switch action {
		case YES:
			{
				successmsg, err = sandboxAction(conf, dryrun, true, "Sandbox Recreation")
				if util.ErrorCheck(err) {
					return cli.NewExitError(err.Error(), 1)
				}
				return cli.NewExitError(successmsg, 0)
			}
		}
		return cli.NewExitError("Sandbox Recreation cancelled.", 0)

	}
	return cli.NewExitError("No known parameters supplied.  Please refer to help for sandbox options.", 1)
}

func sandboxAction(conf config.Config, dryrun bool, recreate bool, actionTitle string) (successmsg string, err error) {
	util.LogInfo(actionTitle)

	// Kick off a migration to recreate the db

	// Check that a local schema exists
	forwardOps, backwardOps, err := diffSchema(conf, actionTitle, recreate)

	if util.ErrorCheck(err) {
		return successmsg, err
	}

	// Create a local migration
	m, err := createMigration(conf, actionTitle, dryrun, forwardOps, backwardOps)

	if util.ErrorCheck(err) {
		return successmsg, err
	}

	// If a clean migration
	if recreate {
		// Recreate the Sandbox Database
		recreateProjectDatabase(conf, dryrun)
	}

	// Apply the migration to the sandbox
	err = migrateSandbox(actionTitle, dryrun, &m)
	if util.ErrorCheck(err) {
		return successmsg, err
	}
	dr := ""
	if dryrun {
		dr = "(DRYRUN)"
	}
	successmsg = fmt.Sprintf("%s %s: Migration successfully with ID: %d", dr, actionTitle, m.MID)
	util.LogInfo(successmsg)

	return successmsg, err
}

func migrateSandbox(actionTitle string, dryrun bool, m *migration.Migration) (err error) {
	util.LogInfof(formatMessage(dryrun, actionTitle, "Applying Schema"))

	util.LogInfo(actionTitle)

	exec.Exec(exec.Options{
		Dryrun:           dryrun,
		Force:            true,
		Rollback:         true,
		PTODisabled:      true,
		AllowDestructive: true,
		Sandbox:          true,
		Migration:        m,
	})
	if util.ErrorCheck(err) {
		err = fmt.Errorf("%s: Execute failed. Unable to execute sandbox Migration with ID: [%d]", actionTitle, m.MID)
	}

	return err
}

func diffSchema(conf config.Config, actionTitle string, recreate bool) (forwardOps mysql.SQLOperations, backwardOps mysql.SQLOperations, err error) {

	var forwardDiff table.Differences
	var backwardDiff table.Differences
	// Read the YAML schema
	err = yaml.ReadTables(conf.Project.LocalSchema.Path)
	if util.ErrorCheck(err) {
		err = fmt.Errorf("%s failed. Unable to read YAML Tables", actionTitle)
	}

	// If schema was found
	if len(yaml.Schema) > 0 {
		// Validate the YAML Schema
		_, err = id.ValidateSchema(yaml.Schema, "YAML Schema")
		if util.ErrorCheck(err) {
			err = fmt.Errorf("%s failed. YAML Validation Errors Detected", actionTitle)
		}

		// Read the MySQL tables from the target database
		err = mysql.ReadTables()
		if util.ErrorCheck(err) {
			err = fmt.Errorf("%s failed. Unable to read MySQL Tables", actionTitle)
		}

		// Don't bother validating the database if we're going to wipe it.
		if !recreate {
			// Validate the MySQL Schema
			_, err = id.ValidateSchema(mysql.Schema, "Target Database Schema")
			if util.ErrorCheck(err) {
				err = fmt.Errorf("%s failed. Target Database Validation Errors Detected", actionTitle)
			}
		}

		// Generate Diffs
		forwardDiff, err = table.DiffTables(yaml.Schema, mysql.Schema)
		if util.ErrorCheckf(err, "Diff Failed while generating forward migration") {
			return forwardOps, backwardOps, err
		}
		forwardOps = mysql.GenerateAlters(forwardDiff)

		backwardDiff, err = table.DiffTables(mysql.Schema, yaml.Schema)
		if util.ErrorCheckf(err, "Diff Failed while generating backward migration") {
			return forwardOps, backwardOps, err
		}
		backwardOps = mysql.GenerateAlters(backwardDiff)
	}

	return forwardOps, backwardOps, err
}

func createMigration(conf config.Config, actionTitle string, dryrun bool, forwardOps mysql.SQLOperations, backwardOps mysql.SQLOperations) (m migration.Migration, err error) {
	if !dryrun {
		util.LogInfo(formatMessage(dryrun, "Sandbox Create Migration", "Inserting new Migration into the DB"))
		// Create a temporary migration.  If there a way we can avoid this?
		m, err = migration.New(migration.Param{
			Project: conf.Project.Name,
			Version: conf.Project.Schema.Version,
			// Use the current state of the local Git repo (Don't do a git checkout )
			// Migration Database doesn't need to have any git info in it because this feature is for testing
			// migrations without having checked them in
			Timestamp:   time.Now().UTC().Format(mysql.TimeFormat),
			Description: actionTitle,
			Forwards:    forwardOps,
			Backwards:   backwardOps,
			Sandbox:     true,
		})

		if util.ErrorCheck(err) {
			err = fmt.Errorf("%s failed. Unable to create new Migration in the management database", actionTitle)
		} else {
			util.LogInfof("%s: Created Migration with ID: %d", actionTitle, m.MID)
		}

	} else {
		util.LogInfof("(DRYRUN) Skipping Creating Migration")
	}

	return m, err
}

func recreateProjectDatabase(conf config.Config, dryrun bool) (err error) {
	var output string

	dropCommand := fmt.Sprintf("DROP DATABASE `%s`", conf.Project.DB.Database)
	createCommand := fmt.Sprintf("CREATE DATABASE `%s`", conf.Project.DB.Database)

	util.LogInfo(formatMessage(dryrun, "Sandbox Recreation", "Recreating Database"))
	if !dryrun {
		output, err = exec.ExecuteSQL(dropCommand, false)
		if util.ErrorCheckf(err, "Problem dropping DATABASE for Project: [%s] SQL: [%s] Output: [%s]", conf.Project.Name, dropCommand, output) {
			return cli.NewExitError("Sandbox Recreation failed. Couldn't DROP Project Database", 1)
		}

		output, err = exec.ExecuteSQL(createCommand, false)
		if util.ErrorCheckf(err, "Problem creating DATABASE for Project: [%s] SQL: [%s] Output: [%s]", conf.Project.Name, createCommand, output) {
			return cli.NewExitError("Sandbox Recreation failed. Couldn't Create Project Database", 1)
		}

		// Force a Reconnect to the database because the DB was just recreated
		exec.ConnectProjectDB(true)

	} else {
		util.LogInfof("(DRYRUN) Exec SQL: %s", dropCommand)
		util.LogInfof("(DRYRUN) Exec SQL: %s", createCommand)
	}

	return err
}

func formatMessage(dryrun bool, context string, message string, info ...interface{}) string {
	message = fmt.Sprintf("%s: %s", context, fmt.Sprintf(message, info...))
	if dryrun {
		message = "(DRYRUN) " + message
	}
	return message
}
<|endoftext|>