<commit_before>package tview

import (
	"github.com/gdamore/tcell"
)

// MouseAction are bit flags indicating what the mouse is logically doing.
type MouseAction int32

const (
	MouseMove MouseAction = 1 << iota
	MouseLeftDown
	MouseLeftUp
	MouseLeftClick
	MouseLeftDoubleClick
	MouseMiddleDown
	MouseMiddleUp
	MouseMiddleClick
	MouseMiddleDoubleClick
	MouseRightDown
	MouseRightUp
	MouseRightClick
	MouseRightDoubleClick
	WheelUp
	WheelDown
	WheelLeft
	WheelRight
)

// Does not set MouseMove or *Click actions.
func getMouseButtonAction(lastBtn, btn tcell.ButtonMask) MouseAction {
	btnDiff := btn ^ lastBtn
	var action MouseAction

	if btnDiff&tcell.Button1 != 0 {
		if btn&tcell.Button1 != 0 {
			action |= MouseLeftDown
		} else {
			action |= MouseLeftUp
		}
	}

	if btnDiff&tcell.Button2 != 0 {
		if btn&tcell.Button2 != 0 {
			action |= MouseMiddleDown
		} else {
			action |= MouseMiddleUp
		}
	}

	if btnDiff&tcell.Button3 != 0 {
		if btn&tcell.Button3 != 0 {
			action |= MouseRightDown
		} else {
			action |= MouseRightUp
		}
	}

	if btn&tcell.WheelUp != 0 {
		action |= WheelUp
	}
	if btn&tcell.WheelDown != 0 {
		action |= WheelDown
	}
	if btn&tcell.WheelLeft != 0 {
		action |= WheelLeft
	}
	if btn&tcell.WheelRight != 0 {
		action |= WheelRight
	}

	return action
}

// Do not call if the mouse moved.
// Sets the *Click, including *DoubleClick.
// This should be called last, after setting all the other flags.
func getMouseClickAction(lastAct, action MouseAction) MouseAction {
	if action&MouseMove == 0 {
		if action&MouseLeftUp != 0 {
			if lastAct&(MouseLeftClick&MouseLeftDoubleClick) == 0 {
				action |= MouseLeftClick
			} else if lastAct&MouseLeftDoubleClick == 0 {
				action |= MouseLeftDoubleClick
			}
		}
		if action&MouseMiddleUp != 0 {
			if lastAct&(MouseMiddleClick&MouseMiddleDoubleClick) == 0 {
				action |= MouseMiddleClick
			} else if lastAct&MouseMiddleDoubleClick == 0 {
				action |= MouseMiddleDoubleClick
			}
		}
		if action&MouseRightUp != 0 {
			if lastAct&(MouseRightClick&MouseRightDoubleClick) == 0 {
				action |= MouseRightClick
			} else if lastAct&MouseRightDoubleClick == 0 {
				action |= MouseRightDoubleClick
			}
		}
	}
	return action
}
<commit_msg>Fix operator<commit_after>package tview

import (
	"github.com/gdamore/tcell"
)

// MouseAction are bit flags indicating what the mouse is logically doing.
type MouseAction int32

const (
	MouseMove MouseAction = 1 << iota
	MouseLeftDown
	MouseLeftUp
	MouseLeftClick
	MouseLeftDoubleClick
	MouseMiddleDown
	MouseMiddleUp
	MouseMiddleClick
	MouseMiddleDoubleClick
	MouseRightDown
	MouseRightUp
	MouseRightClick
	MouseRightDoubleClick
	WheelUp
	WheelDown
	WheelLeft
	WheelRight
)

// Does not set MouseMove or *Click actions.
func getMouseButtonAction(lastBtn, btn tcell.ButtonMask) MouseAction {
	btnDiff := btn ^ lastBtn
	var action MouseAction

	if btnDiff&tcell.Button1 != 0 {
		if btn&tcell.Button1 != 0 {
			action |= MouseLeftDown
		} else {
			action |= MouseLeftUp
		}
	}

	if btnDiff&tcell.Button2 != 0 {
		if btn&tcell.Button2 != 0 {
			action |= MouseMiddleDown
		} else {
			action |= MouseMiddleUp
		}
	}

	if btnDiff&tcell.Button3 != 0 {
		if btn&tcell.Button3 != 0 {
			action |= MouseRightDown
		} else {
			action |= MouseRightUp
		}
	}

	if btn&tcell.WheelUp != 0 {
		action |= WheelUp
	}
	if btn&tcell.WheelDown != 0 {
		action |= WheelDown
	}
	if btn&tcell.WheelLeft != 0 {
		action |= WheelLeft
	}
	if btn&tcell.WheelRight != 0 {
		action |= WheelRight
	}

	return action
}

// Do not call if the mouse moved.
// Sets the *Click, including *DoubleClick.
// This should be called last, after setting all the other flags.
func getMouseClickAction(lastAct, action MouseAction) MouseAction {
	if action&MouseMove == 0 {
		if action&MouseLeftUp != 0 {
			if lastAct&(MouseLeftClick|MouseLeftDoubleClick) == 0 {
				action |= MouseLeftClick
			} else if lastAct&MouseLeftDoubleClick == 0 {
				action |= MouseLeftDoubleClick
			}
		}
		if action&MouseMiddleUp != 0 {
			if lastAct&(MouseMiddleClick|MouseMiddleDoubleClick) == 0 {
				action |= MouseMiddleClick
			} else if lastAct&MouseMiddleDoubleClick == 0 {
				action |= MouseMiddleDoubleClick
			}
		}
		if action&MouseRightUp != 0 {
			if lastAct&(MouseRightClick|MouseRightDoubleClick) == 0 {
				action |= MouseRightClick
			} else if lastAct&MouseRightDoubleClick == 0 {
				action |= MouseRightDoubleClick
			}
		}
	}
	return action
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/t3rm1n4l/go-mega"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

const CACHEDIR = "cache"

var megaSession *mega.Mega

func handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		get(w, r)
	case "PUT":
		put(w, r)
	}
}

func list(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	children, err := megaSession.FS.GetChildren(node)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Print(err)
		return
	}

	fmt.Fprint(w, "")
	fmt.Fprintf(w, "
%s
", html.EscapeString(r.URL.Path))
	fmt.Fprint(w, "
")
	if node != megaSession.FS.GetRoot() {
		fmt.Fprintf(w, "
..")
	}
	for _, child := range children {
		var folder string
		if child.GetType() == mega.FOLDER {
			folder = "/"
		}
		fmt.Fprintf(w, "
%s%s",
			html.EscapeString(child.GetName()), folder,
			html.EscapeString(child.GetName()), folder)
	}
	fmt.Fprint(w, "
")
}

func get(w http.ResponseWriter, r *http.Request) {
	node, err := lookup(r.URL.Path)
	if err != nil {
		if err.Error() == "Object (typically, node or user) not found" {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		log.Print(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// List directories
	switch node.GetType() {
	case mega.FOLDER, mega.ROOT:
		list(w, r, node)
		return
	}

	// Cache files
	cachefile := CACHEDIR + r.URL.Path
	dir, _ := path.Split(cachefile)

	// Do we have this cached?
	file, err := os.Open(cachefile)
	if err != nil && os.IsNotExist(err) {
		// Build directory structure first
		if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// Download file
		if err = megaSession.DownloadFile(node, cachefile, nil); err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			// Remove incomplete cachefile, in case one was created
			os.Remove(cachefile)
			return
		}

		// Open cached file
		file, err = os.Open(cachefile)
		if err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
	defer file.Close()
	_, err = io.Copy(w, file)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func put(w http.ResponseWriter, r *http.Request) {
	cachefile := CACHEDIR + r.URL.Path
	dir, name := path.Split(cachefile)

	// Create local file
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	fp, err := os.Create(cachefile)
	if err != nil {
		log.Print(err)
		return
	}
	_, err = io.Copy(fp, r.Body)
	if err != nil {
		log.Print(err)
		return
	}

	// Create Mega path
	dirarray := strings.Split(r.URL.Path, "/")
	root := megaSession.FS.GetRoot()
	n, err := mkpath(dirarray[1:len(dirarray)-1], root)
	if err != nil {
		log.Print(err)
		return
	}

	// Lookup Mega file (if it exists)
	paths, err := megaSession.FS.PathLookup(root, dirarray[1:])
	// Log unexpected errors
	if err != nil && err.Error() != "Object (typically, node or user) not found" {
		log.Print(err)
		return
	}
	// File exists, so delete it before uploading new file
	if err == nil {
		// We only care about the last node.
		lastnode := paths[len(paths)-1]
		// File exists, delete! (aka overwrite)
		if err = megaSession.Delete(lastnode, false); err != nil {
			log.Print(err)
			return
		}
	}
	// Finally, upload file
	_, err = megaSession.UploadFile(cachefile, n, name, nil)
	if err != nil {
		log.Print(err)
		return
	}
}

func mkpath(p []string, parent *mega.Node) (*mega.Node, error) {
	var n *mega.Node
	var err error

	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if p[0] == "" {
		return root, nil
	}

	paths, err := megaSession.FS.PathLookup(root, p)
	// Path found
	if err == nil {
		// We only care about the last path.
		return paths[len(paths)-1], nil
	} else if err.Error() != "Object (typically, node or user) not found" {
		// Expected "not found" error, got something else
		return nil, err
	}

	l := len(p)
	if l == 1 {
		n = parent
	} else {
		// if a/b/c then parent = mkpath(a/b)
		n, err = mkpath(p[:l-1], parent)
		if err != nil {
			return n, err
		}
	}
	return megaSession.CreateDir(p[l-1], n)
}

func lookup(url string) (*mega.Node, error) {
	trimmedPath := strings.Trim(url, "/")
	path := strings.Split(trimmedPath, "/")
	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if path[0] == "" {
		return root, nil
	} else {
		paths, err := megaSession.FS.PathLookup(root, path)
		if err != nil {
			return nil, err
		}
		// We only care about the last path.
		return paths[len(paths)-1], nil
	}
}

func main() {
	user := os.Getenv("MEGA_USER")
	pass := os.Getenv("MEGA_PASSWD")
	megaSession = mega.New()
	if err := megaSession.Login(user, pass); err != nil {
		log.Fatal(err)
	}
	if err := os.Mkdir(CACHEDIR, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
	}
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
<commit_msg>Detect open errors.<commit_after>package main

import (
	"fmt"
	"github.com/t3rm1n4l/go-mega"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

const CACHEDIR = "cache"

var megaSession *mega.Mega

func handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		get(w, r)
	case "PUT":
		put(w, r)
	}
}

func list(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	children, err := megaSession.FS.GetChildren(node)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Print(err)
		return
	}

	fmt.Fprint(w, "")
	fmt.Fprintf(w, "
%s
", html.EscapeString(r.URL.Path))
	fmt.Fprint(w, "
")
	if node != megaSession.FS.GetRoot() {
		fmt.Fprintf(w, "
..")
	}
	for _, child := range children {
		var folder string
		if child.GetType() == mega.FOLDER {
			folder = "/"
		}
		fmt.Fprintf(w, "
%s%s",
			html.EscapeString(child.GetName()), folder,
			html.EscapeString(child.GetName()), folder)
	}
	fmt.Fprint(w, "
")
}

func get(w http.ResponseWriter, r *http.Request) {
	node, err := lookup(r.URL.Path)
	if err != nil {
		if err.Error() == "Object (typically, node or user) not found" {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		log.Print(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// List directories
	switch node.GetType() {
	case mega.FOLDER, mega.ROOT:
		list(w, r, node)
		return
	}

	// Cache files
	cachefile := CACHEDIR + r.URL.Path
	dir, _ := path.Split(cachefile)

	// Do we have this cached?
	file, err := os.Open(cachefile)
	if err != nil {
		// Unexpected error
		if !os.IsNotExist(err) {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// Expected error: file not found = cache miss
		// Build directory structure first
		if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// Download file
		if err = megaSession.DownloadFile(node, cachefile, nil); err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			// Remove incomplete cachefile, in case one was created
			os.Remove(cachefile)
			return
		}

		// Open cached file
		file, err = os.Open(cachefile)
		if err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
	defer file.Close()
	_, err = io.Copy(w, file)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func put(w http.ResponseWriter, r *http.Request) {
	cachefile := CACHEDIR + r.URL.Path
	dir, name := path.Split(cachefile)

	// Create local file
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	fp, err := os.Create(cachefile)
	if err != nil {
		log.Print(err)
		return
	}
	_, err = io.Copy(fp, r.Body)
	if err != nil {
		log.Print(err)
		return
	}

	// Create Mega path
	dirarray := strings.Split(r.URL.Path, "/")
	root := megaSession.FS.GetRoot()
	n, err := mkpath(dirarray[1:len(dirarray)-1], root)
	if err != nil {
		log.Print(err)
		return
	}

	// Lookup Mega file (if it exists)
	paths, err := megaSession.FS.PathLookup(root, dirarray[1:])
	// Log unexpected errors
	if err != nil && err.Error() != "Object (typically, node or user) not found" {
		log.Print(err)
		return
	}
	// File exists, so delete it before uploading new file
	if err == nil {
		// We only care about the last node.
		lastnode := paths[len(paths)-1]
		// File exists, delete! (aka overwrite)
		if err = megaSession.Delete(lastnode, false); err != nil {
			log.Print(err)
			return
		}
	}
	// Finally, upload file
	_, err = megaSession.UploadFile(cachefile, n, name, nil)
	if err != nil {
		log.Print(err)
		return
	}
}

func mkpath(p []string, parent *mega.Node) (*mega.Node, error) {
	var n *mega.Node
	var err error

	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if p[0] == "" {
		return root, nil
	}

	paths, err := megaSession.FS.PathLookup(root, p)
	// Path found
	if err == nil {
		// We only care about the last path.
		return paths[len(paths)-1], nil
	} else if err.Error() != "Object (typically, node or user) not found" {
		// Expected "not found" error, got something else
		return nil, err
	}

	l := len(p)
	if l == 1 {
		n = parent
	} else {
		// if a/b/c then parent = mkpath(a/b)
		n, err = mkpath(p[:l-1], parent)
		if err != nil {
			return n, err
		}
	}
	return megaSession.CreateDir(p[l-1], n)
}

func lookup(url string) (*mega.Node, error) {
	trimmedPath := strings.Trim(url, "/")
	path := strings.Split(trimmedPath, "/")
	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if path[0] == "" {
		return root, nil
	} else {
		paths, err := megaSession.FS.PathLookup(root, path)
		if err != nil {
			return nil, err
		}
		// We only care about the last path.
		return paths[len(paths)-1], nil
	}
}

func main() {
	user := os.Getenv("MEGA_USER")
	pass := os.Getenv("MEGA_PASSWD")
	megaSession = mega.New()
	if err := megaSession.Login(user, pass); err != nil {
		log.Fatal(err)
	}
	if err := os.Mkdir(CACHEDIR, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
	}
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

type Option struct {
	Name     string
	Question string
	Default  string
}

var options = []Option{
	{
		Name:     "prod",
		Question: "What is your production environment branch?",
		Default:  "master",
	},
	{
		Name:     "other",
		Question: "What other environment branches do you have?",
		Default:  "stage dev",
	},
	{
		Name:     "types",
		Question: "What branch types do you have?",
		Default:  "feature hotfix",
	},
}

type Config struct {
	Prod  string
	Other []string
	Types []string
}

var config Config

func main() {
	if len(os.Args) < 2 {
		help()
	}

	switch os.Args[1] {
	case "init":
		cmdInit()
	case "branch":
		readConfig()
		cmdBranch(os.Args[2:])
	default:
		help()
	}
}

// Commands

func cmdInit() {
	values := map[string]string{}
	reader := bufio.NewReader(os.Stdin)

	for _, opt := range options {
		fmt.Printf("%s [%s] ", opt.Question, opt.Default)
		value, err := reader.ReadString('\n')
		if err != nil {
			panic(err)
		}
		if value == "\n" {
			values[opt.Name] = opt.Default
		} else {
			values[opt.Name] = value[:len(value)-1]
		}
	}

	for k, v := range values {
		err := exec.Command("git", "config", "--local", "--replace-all", "env-branch."+k, v).Run()
		if err != nil {
			panic(err)
		}
	}

	fmt.Println("You're ready to go.")
}

func cmdBranch(args []string) {
	if len(args) < 1 {
		help()
	}

	switch args[0] {
	case "start":
		if len(args) < 2 {
			help()
		}
		newBranch := args[1]

		runCommand("git", "checkout", config["prod"])
		runCommand("git", "pull", "--rebase", "origin", config["prod"])
		runCommand("git", "checkout", "-b", newBranch)

	case "deploy":

		if len(args) < 2 {
			help()
		}

		deployEnv := args[1]
		var feature string

		if len(args) > 2 {
			feature = args[2]
		} else {
			feature, err = getCurrentBranch()
			if err != nil {
				panic(err)
			}
		}

		runCommand("git", "checkout", feature)
		runCommand("git", "pull", "--rebase", "origin", config["prod"])
		runCommand("git", "checkout", deployEnv)
		runCommand("git", "pull", "--rebase", "origin", deployEnv)
		runCommand("git", "merge", feature)
		runCommand("git", "push", "origin", deployEnv)

	default:
		help()
	}
}

// Everything else

func help() {
	fmt.Println("TODO: implement help")
	os.Exit(1)
}

func runCommand(cmd ...string) {
	err := exec.Command(cmd[0], cmd[1:]...).Run()
	if err != nil {
		log.Fatalf("Failed executing command: %#v\n", cmd)
	}
}

func readConfig() {
	config = Config{}

	cfg := map[string]string{}

	for _, opt := range options {
		stdout, err := exec.Command("git", "config", "env-branch."+opt.Name).Output()
		if err != nil {
			log.Fatalf("This repo isn't git env enabled. Run 'git env init' first.")
		}
		cfg[opt.Name] = string(stdout)[:len(stdout)-1]
	}
	config.Prod = cfg["prod"]
	config.Other = strings.Split(cfg["other"], " ")
	config.Types = strings.Split(cfg["types"], " ")
}

func getCurrentBranch() (string, error) {
	stdout, err := exec.Command("git", "branch").Output()
	if err != nil {
		return "", err
	}

	lines := strings.Split(string(stdout), "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "* ") {
			items := strings.Split(line, " ")

			//TODO
			// return error if the branch is an environment

			return items[1], nil
		}
	}

	return "", errors.New("could not detect current branch")
}
<commit_msg>Run git commands with full output<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

type Option struct {
	Name     string
	Question string
	Default  string
}

var options = []Option{
	{
		Name:     "prod",
		Question: "What is your production environment branch?",
		Default:  "master",
	},
	{
		Name:     "other",
		Question: "What other environment branches do you have?",
		Default:  "stage dev",
	},
	{
		Name:     "types",
		Question: "What branch types do you have?",
		Default:  "feature hotfix",
	},
}

type Config struct {
	Prod  string
	Other []string
	Types []string
}

var config Config

func main() {
	if len(os.Args) < 2 {
		help()
	}

	switch os.Args[1] {
	case "init":
		cmdInit()
	case "branch":
		readConfig()
		cmdBranch(os.Args[2:])
	default:
		help()
	}
}

// Commands

func cmdInit() {
	values := map[string]string{}
	reader := bufio.NewReader(os.Stdin)

	for _, opt := range options {
		fmt.Printf("%s [%s] ", opt.Question, opt.Default)
		value, err := reader.ReadString('\n')
		if err != nil {
			panic(err)
		}
		if value == "\n" {
			values[opt.Name] = opt.Default
		} else {
			values[opt.Name] = value[:len(value)-1]
		}
	}

	for k, v := range values {
		err := exec.Command("git", "config", "--local", "--replace-all", "env-branch."+k, v).Run()
		if err != nil {
			panic(err)
		}
	}

	fmt.Println("You're ready to go.")
}

func cmdBranch(args []string) {
	if len(args) < 1 {
		help()
	}

	switch args[0] {
	case "start":
		if len(args) < 2 {
			help()
		}
		newBranch := args[1]

		gitCommand("checkout", config.Prod)
		gitCommand("pull", "--rebase", "origin", config.Prod)
		gitCommand("checkout", "-b", newBranch)

	case "deploy":

		if len(args) < 2 {
			help()
		}

		deployEnv := args[1]
		var feature string
		var err error

		if len(args) > 2 {
			feature = args[2]
		} else {
			feature, err = getCurrentBranch()
			if err != nil {
				panic(err)
			}
		}

		gitCommand("checkout", feature)
		gitCommand("pull", "--rebase", "origin", config.Prod)
		gitCommand("checkout", deployEnv)
		gitCommand("pull", "--rebase", "origin", deployEnv)
		gitCommand("merge", feature)
		gitCommand("push", "origin", deployEnv)

	default:
		help()
	}
}

// Everything else

func help() {
	fmt.Println("TODO: implement help")
	os.Exit(1)
}

func gitCommand(args ...string) {
	fmt.Printf("+ git %s\n", strings.Join(args, " "))
	cmd := exec.Command("git", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	err := cmd.Run()

	if err != nil {
		log.Fatalf("Failed executing command: git %s\n", strings.Join(args, " "))
	}
}

func readConfig() {
	config = Config{}

	cfg := map[string]string{}

	for _, opt := range options {
		stdout, err := exec.Command("git", "config", "env-branch."+opt.Name).Output()
		if err != nil {
			log.Fatalf("This repo isn't git env enabled. Run 'git env init' first.")
		}
		cfg[opt.Name] = string(stdout)[:len(stdout)-1]
	}
	config.Prod = cfg["prod"]
	config.Other = strings.Split(cfg["other"], " ")
	config.Types = strings.Split(cfg["types"], " ")
}

func getCurrentBranch() (string, error) {
	stdout, err := exec.Command("git", "branch").Output()
	if err != nil {
		return "", err
	}

	lines := strings.Split(string(stdout), "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "* ") {
			items := strings.Split(line, " ")

			//TODO
			// return error if the branch is an environment

			return items[1], nil
		}
	}

	return "", errors.New("could not detect current branch")
}
<|endoftext|>


<commit_before>package main

import (
	"sync"
)

func Spawner() {
	log.Debug("Spawner started")

	var wg sync.WaitGroup

	for name, server := range Config.Server {
		c := make(chan Article)

		log.Info("[%s] Starting %d connections...", name, server.Connections)

		for i := 0; i < server.Connections; i++ {
			// Increment the WaitGroup
			wg.Add(1)

			// Launch a goroutine for this connection
			go func(c chan Article) {
				// Decrement the counter when the goroutine completes
				defer wg.Done()

				// Create server connection
				// nntp := SimpleNNTP()

				// Connect
				// if server.TLS {
				// 	nntp.DialTLS(server.Address, server.Port)
				// } else {
				// 	nntp.Dial(server.Address, server.Port)
				// }

				// Authenticate if required
				// if server.Username && server.Password {
				// 	nntp.Auth(server.Username, server.Password)
				// }

				// Begin consuming
				for article := range c {
					log.Info("[%s] %+v", name, article)
				}
			}(c)

			log.Debug("[%s] Started connection #%d", name, i+1)
		}
	}

	// Wait for all connections to complete
	wg.Wait()
}
<commit_msg>Implement basic connection stuff<commit_after>package main

import (
	"github.com/madcowfred/gopoststuff/simplenntp"
	"sync"
)

func Spawner() {
	log.Debug("Spawner started")

	var wg sync.WaitGroup

	for name, server := range Config.Server {
		c := make(chan Article)

		log.Info("[%s] Starting %d connections", name, server.Connections)

		for i := 0; i < server.Connections; i++ {
			connID := i + 1

			// Increment the WaitGroup
			wg.Add(1)

			// Launch a goroutine for this connection
			go func(c chan Article) {
				// Decrement the counter when the goroutine completes
				defer wg.Done()

				// Connect
				log.Debug("[%s:%02d] Connecting...", name, connID)
				conn, err := simplenntp.Dial(server.Address, server.Port, server.TLS)
				if err != nil {
					log.Critical("[%s] Error while connecting: %s", name, err)
				}
				log.Debug("[%s:%02d] Connected", name, connID)

				// Authenticate if required
				if len(server.Username) > 0 {
					log.Debug("[%s:%02d] Authenticating...", name, connID)
					err := conn.Authenticate(server.Username, server.Password)
					if err != nil {
						log.Fatalf("[%s:%02d] Error while authenticating: %s", name, connID, err)
					}
					log.Debug("[%s:%02d] Authenticated", name, connID)
				}

				// Begin consuming
				for article := range c {
					log.Info("[%s] %+v", name, article)
				}
			}(c)
		}
	}

	// Wait for all connections to complete
	wg.Wait()
}
<|endoftext|>


<commit_before>package wrap

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	mackerel "github.com/mackerelio/mackerel-client-go"
	cli "gopkg.in/urfave/cli.v1"
)

func newWrapContext(args []string) *cli.Context {
	app := cli.NewApp()
	parentFs := flag.NewFlagSet("mockmkr", flag.ContinueOnError)
	for _, f := range []cli.Flag{
		cli.StringFlag{Name: "conf"}, cli.StringFlag{Name: "apibase"},
	} {
		f.Apply(parentFs)
	}
	parentFs.Parse(args)
	for i, v := range parentFs.Args() {
		if v == "wrap" {
			args = parentFs.Args()[i+1:]
			break
		}
	}
	parentCtx := cli.NewContext(app, parentFs, nil)

	fs := flag.NewFlagSet("mockwrap", flag.ContinueOnError)
	for _, f := range Command.Flags {
		f.Apply(fs)
	}
	fs.Parse(args)
	return cli.NewContext(app, fs, parentCtx)
}

func TestCommand_Action(t *testing.T) {
	type testResult struct {
		Name                 string               `json:"name"`
		Status               mackerel.CheckStatus `json:"status"`
		Message              string               `json:"message"`
		NotificationInterval uint                 `json:"notificationInterval,omitempty"`
	}
	type testReq struct {
		Reports []testResult `json:"reports"`
	}

	testCases := []struct {
		Name           string
		Args           []string
		ExpectedResult testResult
	}{
		{
			Name: "simple",
			Args: []string{
				"-name=test-check",
				"-detail",
				"-memo", "This is memo",
				"--",
				"go", "run", "testdata/stub.go",
			},
			ExpectedResult: testResult{
				Name:   "test-check",
				Status: mackerel.CheckStatusCritical,
				Message: `command exited with code: 1
Memo: This is memo
% go run testdata/stub.go
Hello.
exit status 1
`,
				NotificationInterval: 0,
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.Name, func(t *testing.T) {
			ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
				reqPath := "/api/v0/monitoring/checks/report"
				if req.URL.Path != reqPath {
					t.Errorf("request URL should be %s but: %s", reqPath, req.URL.Path)
				}

				body, _ := ioutil.ReadAll(req.Body)
				var treq testReq

				err := json.Unmarshal(body, &treq)
				if err != nil {
					t.Fatal("request body should be decoded as json", string(body))
				}
				got := treq.Reports[0]
				expect := tc.ExpectedResult

				if !reflect.DeepEqual(got, expect) {
					t.Errorf("something went wrong.\n   got: %+v,\nexpect: %+v", got, expect)
				}

				res.Header()["Content-Type"] = []string{"application/json"}
				json.NewEncoder(res).Encode(map[string]bool{
					"success": true,
				})
			}))
			defer ts.Close()

			args := append(
				[]string{"-conf=testdata/dummy.conf", "-apibase", ts.URL, "wrap"},
				tc.Args...,
			)

			c := newWrapContext(args)
			Command.Action.(func(*cli.Context) error)(c)
		})
	}
}
<commit_msg>[wrap] add tests around notification interval<commit_after>package wrap

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	mackerel "github.com/mackerelio/mackerel-client-go"
	cli "gopkg.in/urfave/cli.v1"
)

func newWrapContext(args []string) *cli.Context {
	app := cli.NewApp()
	parentFs := flag.NewFlagSet("mockmkr", flag.ContinueOnError)
	for _, f := range []cli.Flag{
		cli.StringFlag{Name: "conf"}, cli.StringFlag{Name: "apibase"},
	} {
		f.Apply(parentFs)
	}
	parentFs.Parse(args)
	for i, v := range parentFs.Args() {
		if v == "wrap" {
			args = parentFs.Args()[i+1:]
			break
		}
	}
	parentCtx := cli.NewContext(app, parentFs, nil)

	fs := flag.NewFlagSet("mockwrap", flag.ContinueOnError)
	for _, f := range Command.Flags {
		f.Apply(fs)
	}
	fs.Parse(args)
	return cli.NewContext(app, fs, parentCtx)
}

func TestCommand_Action(t *testing.T) {
	type testResult struct {
		Name                 string               `json:"name"`
		Status               mackerel.CheckStatus `json:"status"`
		Message              string               `json:"message"`
		NotificationInterval uint                 `json:"notificationInterval,omitempty"`
	}
	type testReq struct {
		Reports []testResult `json:"reports"`
	}

	testCases := []struct {
		Name string
		Args []string

		Result   testResult
		ExitCode int
	}{
		{
			Name: "simple",
			Args: []string{
				"-name=test-check",
				"-detail",
				"-memo", "This is memo",
				"--",
				"go", "run", "testdata/stub.go",
			},
			Result: testResult{
				Name:   "test-check",
				Status: mackerel.CheckStatusCritical,
				Message: `command exited with code: 1
Memo: This is memo
% go run testdata/stub.go
Hello.
exit status 1
`,
				NotificationInterval: 0,
			},
			ExitCode: 1,
		},
		{
			Name: "notification interval",
			Args: []string{
				"-name=test-check2",
				"-auto-close",
				"-notification-interval", "20m",
				"--",
				"echo", "1",
			},
			Result: testResult{
				Name:   "test-check2",
				Status: mackerel.CheckStatusOK,
				Message: `command exited with code: 0
% echo 1
`,
				NotificationInterval: 1200,
			},
			ExitCode: 0,
		},
		{
			Name: "minimum notification interval",
			Args: []string{
				"-name=test-check3",
				"-auto-close",
				"-notification-interval", "5m",
				"--",
				"echo", "2",
			},
			Result: testResult{
				Name:   "test-check2",
				Status: mackerel.CheckStatusOK,
				Message: `command exited with code: 0
% echo 2
`,
				NotificationInterval: 600,
			},
			ExitCode: 0,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.Name, func(t *testing.T) {
			ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
				reqPath := "/api/v0/monitoring/checks/report"
				if req.URL.Path != reqPath {
					t.Errorf("request URL should be %s but: %s", reqPath, req.URL.Path)
				}

				body, _ := ioutil.ReadAll(req.Body)
				var treq testReq

				err := json.Unmarshal(body, &treq)
				if err != nil {
					t.Fatal("request body should be decoded as json", string(body))
				}
				got := treq.Reports[0]
				expect := tc.Result

				if !reflect.DeepEqual(got, expect) {
					t.Errorf("something went wrong.\n   got: %+v,\nexpect: %+v", got, expect)
				}

				res.Header()["Content-Type"] = []string{"application/json"}
				json.NewEncoder(res).Encode(map[string]bool{
					"success": true,
				})
			}))
			defer ts.Close()

			args := append(
				[]string{"-conf=testdata/dummy.conf", "-apibase", ts.URL, "wrap"},
				tc.Args...,
			)

			c := newWrapContext(args)
			err := Command.Action.(func(*cli.Context) error)(c)
			var exitCode int
			if err != nil {
				exitCode = 1
				if excoder, ok := err.(cli.ExitCoder); ok {
					exitCode = excoder.ExitCode()
				}
			}
			if exitCode != tc.ExitCode {
				t.Errorf("exit code %d is expected. but: %d", tc.ExitCode, exitCode)
			}
		})
	}
}
<|endoftext|>


<commit_before>//  Copyright (c) 2017 Rackspace
//
//  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 ring

import (
	"bytes"
	"fmt"
	"net"
	"os"
	"os/exec"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/troubling/hummingbird/common/conf"
	ini "github.com/vaughan0/go-ini"
)

func TestLocalHost(t *testing.T) {
	// construct ini and check for running memcache servers
	var buffer bytes.Buffer
	iniFile := conf.Config{File: make(ini.File)}
	for i := 0; i < 4; i++ {
		server := fmt.Sprintf("localhost:%d", 10000+i)
		if i > 0 {
			buffer.WriteString(",")
		}
		buffer.WriteString(server)
		if i < 3 {
			c, err := net.Dial("tcp", server)
			if err != nil {
				t.Skipf("skipping test; no server running at %s", server)
				return
			}
			c.Write([]byte("flush_all\r\n"))
			c.Close()
		}
	}
	section := iniFile.Section(confSection)
	section["dial_timeout"] = "1000"
	section["max_free_connections_per_server"] = "3"
	section["memcache_servers"] = buffer.String()
	ring, err := NewMemcacheRingFromConfig(iniFile)
	if err != nil {
		t.Fatal("Unable to get memcache ring")
	}
	testWithRing(t, ring)
}

func TestUnixSocket(t *testing.T) {
	// construct ini and start memcache servers
	var buffer bytes.Buffer
	iniFile := conf.Config{File: make(ini.File)}
	for i := 0; i < 4; i++ {
		sock := fmt.Sprintf("/tmp/test-memcachering-%d", i)
		defer os.Remove(sock)

		if err := os.Remove(sock); err != nil {
			if !os.IsNotExist(err) {
				t.Fatalf("")
			}
		}
		if i > 0 {
			buffer.WriteString(",")
		}
		buffer.WriteString(sock)
		// don't start a memcache server for last sock
		if i < 3 {
			// start memcache server
			cmd := exec.Command("memcached", "-u", "memcache", "-s", sock)
			if err := cmd.Start(); err != nil {
				t.Fatal("Unable to run memcached")
				return
			}
			defer cmd.Wait()
			defer cmd.Process.Kill()
			// Wait a bit for the socket to appear.
			for i := 1; i < 10; i++ {
				if _, err := os.Stat(sock); err == nil {
					break
				}
				time.Sleep(time.Duration(25*i) * time.Millisecond)
			}
		}
	}
	time.Sleep(time.Duration(25) * time.Millisecond)
	section := iniFile.Section(confSection)
	section["dial_timeout"] = "1000"
	section["max_free_connections_per_server"] = "3"
	section["memcache_servers"] = buffer.String()
	ring, err := NewMemcacheRingFromConfig(iniFile)
	if err != nil {
		t.Fatal("Unable to get memcache ring")
	}
	testWithRing(t, ring)
}

func testWithRing(t *testing.T, ring *memcacheRing) {
	testSetGetDelete(t, ring)
	testGetCacheMiss(t, ring)
	testIncr(t, ring)
	testDecr(t, ring)
	testSetGetMulti(t, ring)
	testManySetGets(t, ring)
	testConnectionLimits(t, ring)
	testExpiration(t, ring)
}

func testSetGetDelete(t *testing.T, ring *memcacheRing) {
	key := "testJsonSetGet"
	setValue := "some_value"
	if err := ring.Set(key, setValue, 0); err != nil {
		t.Error(err)
		return
	}
	if getValue, err := ring.Get(key); err != nil {
		t.Error(err)
		return
	} else {
		assert.EqualValues(t, setValue, getValue)
	}
	if err := ring.Delete(key); err != nil {
		t.Error(err)
		return
	}
	if _, err := ring.Get(key); err != nil {
		assert.EqualValues(t, CacheMiss, err)
	} else {
		t.Errorf("Expected a cache miss")
	}
}

func testGetCacheMiss(t *testing.T, ring *memcacheRing) {
	// make a call to an unset cache value and check for cache miss return
	key := "testGetCacheMiss"
	if _, err := ring.Get(key); err != nil {
		assert.EqualValues(t, CacheMiss, err)
	} else {
		t.Errorf("Expected a cache miss")
	}
}

func testIncr(t *testing.T, ring *memcacheRing) {
	// increment multiple times and check running total
	key := "testIncr"
	var running_total int64 = 0
	for i := int64(1); i <= 10; i++ {
		running_total += int64(i)
		if j, err := ring.Incr(key, i, 2); err != nil {
			t.Error(err)
			return
		} else {
			assert.EqualValues(t, running_total, j)
			if value, err := ring.Incr(key, 0, 2); err != nil {
				t.Error(err)
				return
			} else {
				assert.EqualValues(t, running_total, value)
			}
		}
	}
}

func testDecr(t *testing.T, ring *memcacheRing) {
	key := "testDecr"
	// test decrement on unset value sets value to 0
	if value, err := ring.Decr(key, 1, 2); err != nil {
		t.Error(err)
		return
	} else {
		assert.EqualValues(t, int64(0), value)
	}
	// test running total goes to and stays at zero
	var running_total int64 = 30
	if value, err := ring.Incr(key, running_total, 2); err != nil {
		t.Error(err)
		return
	} else {
		assert.EqualValues(t, running_total, value)
	}
	for i := int64(1); i <= 10; i++ {
		if i < running_total {
			running_total -= i
		} else {
			running_total = 0
		}
		if j, err := ring.Decr(key, i, 2); err != nil {
			t.Error(err)
			return
		} else {
			assert.EqualValues(t, running_total, j)
			if value, err := ring.Incr(key, 0, 2); err != nil {
				t.Error(err)
				return
			} else {
				assert.EqualValues(t, running_total, value)
			}
		}
	}
}

func testSetGetMulti(t *testing.T, ring *memcacheRing) {
	key := "testSetGetMulti"
	// set three values at once
	setValues := map[string]interface{}{
		"key1": "value1",
		"key2": "value2",
		"key3": "value3",
	}
	if err := ring.SetMulti(key, setValues, 0); err != nil {
		t.Error(err)
		return
	}
	// get the three values
	getValues, err := ring.GetMulti(key, []string{"key1", "key2", "key3"})
	if err != nil {
		t.Error(err)
		return
	}
	// verify the values match what was set
	found := []string{}
	for getKey, getValue := range getValues {
		for setKey, setValue := range setValues {
			if getKey == setKey {
				assert.EqualValues(t, setValue, getValue)
				found = append(found, getKey)
			}
		}
	}
	assert.EqualValues(t, len(setValues), len(found))
}

func testManySetGets(t *testing.T, ring *memcacheRing) {
	key := "testManySetGets"
	opCount := 100
	var wg sync.WaitGroup
	setIt := func(i int) {
		defer wg.Done()
		setKey := fmt.Sprintf("%s%d", key, i)
		setValue := fmt.Sprintf("value%d", i)
		if err := ring.Set(setKey, setValue, 0); err != nil {
			t.Error(err)
		}
	}
	// set a bunch of keys
	wg.Add(opCount)
	for i := 1; i <= opCount; i++ {
		go setIt(i)
	}
	wg.Wait()
	getIt := func(i int) {
		defer wg.Done()
		getKey := fmt.Sprintf("%s%d", key, i)
		setValue := fmt.Sprintf("value%d", i)
		if getValue, err := ring.Get(getKey); err != nil {
			t.Error(err)
		} else {
			assert.EqualValues(t, setValue, getValue)
		}
	}
	// get a bunch of keys
	wg.Add(opCount)
	for i := 1; i <= opCount; i++ {
		go getIt(i)
	}
	wg.Wait()
}

func testConnectionLimits(t *testing.T, ring *memcacheRing) {
	key := "testConnectionLimits"
	opCount := 1000
	var wg sync.WaitGroup
	setIt := func(i int) {
		defer wg.Done()
		setKey := fmt.Sprintf("%s%d", key, i)
		if err := ring.Set(setKey, "", 0); err != nil {
			t.Error(err)
		}
	}
	// set a bunck of keys
	wg.Add(opCount)
	for i := 0; i < opCount; i++ {
		go setIt(i)
	}
	wg.Wait()
	// verify connections per server are less than the limit
	for _, server := range ring.servers {
		assert.True(t, server.connectionCount() <= 3)
	}
}

func testExpiration(t *testing.T, ring *memcacheRing) {
	if testing.Short() {
		t.Log("Skipping testExpiration()")
		return
	}
	key := "testExpiration"
	opCount := 100
	var wg sync.WaitGroup
	setIt := func(i int, expiration int) {
		defer wg.Done()
		setKey := fmt.Sprintf("%s%d", key, i)
		if err := ring.Set(setKey, "", expiration); err != nil {
			t.Error(err)
		}
	}
	// test both ways of setting expiration
	for i := 0; i < 2; i++ {
		var expiration int
		if i == 0 {
			expiration = 1
		} else {
			now := time.Now()
			expiration = int(now.Unix()) + 1
		}
		wg.Add(opCount)
		// set a bunch of keys with expiration
		for i := 0; i < opCount; i++ {
			go setIt(i, expiration)
		}
		wg.Wait()
		// sleep so the items will expire
		time.Sleep(2 * time.Second)
		// get a bunch of keys and verify cache miss
		for i := 0; i < opCount; i++ {
			getKey := fmt.Sprintf("%s%d", key, i)
			if _, err := ring.Get(getKey); err != nil {
				assert.EqualValues(t, CacheMiss, err)
			} else {
				t.Errorf("Expected a cache miss")
			}
		}
	}
}
<commit_msg>Skip TestUnixSocket by default<commit_after>//  Copyright (c) 2017 Rackspace
//
//  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 ring

import (
	"bytes"
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/troubling/hummingbird/common/conf"
	ini "github.com/vaughan0/go-ini"
)

func TestLocalHost(t *testing.T) {
	// construct ini and check for running memcache servers
	var buffer bytes.Buffer
	iniFile := conf.Config{File: make(ini.File)}
	for i := 0; i < 4; i++ {
		server := fmt.Sprintf("localhost:%d", 10000+i)
		if i > 0 {
			buffer.WriteString(",")
		}
		buffer.WriteString(server)
		if i < 3 {
			c, err := net.Dial("tcp", server)
			if err != nil {
				t.Skipf("skipping test; no server running at %s", server)
				return
			}
			c.Write([]byte("flush_all\r\n"))
			c.Close()
		}
	}
	section := iniFile.Section(confSection)
	section["dial_timeout"] = "1000"
	section["max_free_connections_per_server"] = "3"
	section["memcache_servers"] = buffer.String()
	ring, err := NewMemcacheRingFromConfig(iniFile)
	if err != nil {
		t.Fatal("Unable to get memcache ring")
	}
	testWithRing(t, ring)
}

func TestUnixSocket(t *testing.T) {
	if ok, err := strconv.ParseBool(os.Getenv("TEST_LONG")); !ok || err != nil {
		t.Log("Skipping TestUnixSocket; set TEST_LONG=true to run")
		return
	}
	// construct ini and start memcache servers
	var buffer bytes.Buffer
	iniFile := conf.Config{File: make(ini.File)}
	for i := 0; i < 4; i++ {
		sock := fmt.Sprintf("/tmp/test-memcachering-%d", i)
		defer os.Remove(sock)

		if err := os.Remove(sock); err != nil {
			if !os.IsNotExist(err) {
				t.Fatalf("")
			}
		}
		if i > 0 {
			buffer.WriteString(",")
		}
		buffer.WriteString(sock)
		// don't start a memcache server for last sock
		if i < 3 {
			// start memcache server
			cmd := exec.Command("memcached", "-u", "memcache", "-s", sock)
			if err := cmd.Start(); err != nil {
				t.Fatal("Unable to run memcached")
				return
			}
			defer cmd.Wait()
			defer cmd.Process.Kill()
			// Wait a bit for the socket to appear.
			for i := 1; i < 10; i++ {
				if _, err := os.Stat(sock); err == nil {
					break
				}
				time.Sleep(time.Duration(25*i) * time.Millisecond)
			}
		}
	}
	time.Sleep(time.Duration(25) * time.Millisecond)
	section := iniFile.Section(confSection)
	section["dial_timeout"] = "1000"
	section["max_free_connections_per_server"] = "3"
	section["memcache_servers"] = buffer.String()
	ring, err := NewMemcacheRingFromConfig(iniFile)
	if err != nil {
		t.Fatal("Unable to get memcache ring")
	}
	testWithRing(t, ring)
}

func testWithRing(t *testing.T, ring *memcacheRing) {
	testSetGetDelete(t, ring)
	testGetCacheMiss(t, ring)
	testIncr(t, ring)
	testDecr(t, ring)
	testSetGetMulti(t, ring)
	testManySetGets(t, ring)
	testConnectionLimits(t, ring)
	testExpiration(t, ring)
}

func testSetGetDelete(t *testing.T, ring *memcacheRing) {
	key := "testJsonSetGet"
	setValue := "some_value"
	if err := ring.Set(key, setValue, 0); err != nil {
		t.Error(err)
		return
	}
	if getValue, err := ring.Get(key); err != nil {
		t.Error(err)
		return
	} else {
		assert.EqualValues(t, setValue, getValue)
	}
	if err := ring.Delete(key); err != nil {
		t.Error(err)
		return
	}
	if _, err := ring.Get(key); err != nil {
		assert.EqualValues(t, CacheMiss, err)
	} else {
		t.Errorf("Expected a cache miss")
	}
}

func testGetCacheMiss(t *testing.T, ring *memcacheRing) {
	// make a call to an unset cache value and check for cache miss return
	key := "testGetCacheMiss"
	if _, err := ring.Get(key); err != nil {
		assert.EqualValues(t, CacheMiss, err)
	} else {
		t.Errorf("Expected a cache miss")
	}
}

func testIncr(t *testing.T, ring *memcacheRing) {
	// increment multiple times and check running total
	key := "testIncr"
	var running_total int64 = 0
	for i := int64(1); i <= 10; i++ {
		running_total += int64(i)
		if j, err := ring.Incr(key, i, 2); err != nil {
			t.Error(err)
			return
		} else {
			assert.EqualValues(t, running_total, j)
			if value, err := ring.Incr(key, 0, 2); err != nil {
				t.Error(err)
				return
			} else {
				assert.EqualValues(t, running_total, value)
			}
		}
	}
}

func testDecr(t *testing.T, ring *memcacheRing) {
	key := "testDecr"
	// test decrement on unset value sets value to 0
	if value, err := ring.Decr(key, 1, 2); err != nil {
		t.Error(err)
		return
	} else {
		assert.EqualValues(t, int64(0), value)
	}
	// test running total goes to and stays at zero
	var running_total int64 = 30
	if value, err := ring.Incr(key, running_total, 2); err != nil {
		t.Error(err)
		return
	} else {
		assert.EqualValues(t, running_total, value)
	}
	for i := int64(1); i <= 10; i++ {
		if i < running_total {
			running_total -= i
		} else {
			running_total = 0
		}
		if j, err := ring.Decr(key, i, 2); err != nil {
			t.Error(err)
			return
		} else {
			assert.EqualValues(t, running_total, j)
			if value, err := ring.Incr(key, 0, 2); err != nil {
				t.Error(err)
				return
			} else {
				assert.EqualValues(t, running_total, value)
			}
		}
	}
}

func testSetGetMulti(t *testing.T, ring *memcacheRing) {
	key := "testSetGetMulti"
	// set three values at once
	setValues := map[string]interface{}{
		"key1": "value1",
		"key2": "value2",
		"key3": "value3",
	}
	if err := ring.SetMulti(key, setValues, 0); err != nil {
		t.Error(err)
		return
	}
	// get the three values
	getValues, err := ring.GetMulti(key, []string{"key1", "key2", "key3"})
	if err != nil {
		t.Error(err)
		return
	}
	// verify the values match what was set
	found := []string{}
	for getKey, getValue := range getValues {
		for setKey, setValue := range setValues {
			if getKey == setKey {
				assert.EqualValues(t, setValue, getValue)
				found = append(found, getKey)
			}
		}
	}
	assert.EqualValues(t, len(setValues), len(found))
}

func testManySetGets(t *testing.T, ring *memcacheRing) {
	key := "testManySetGets"
	opCount := 100
	var wg sync.WaitGroup
	setIt := func(i int) {
		defer wg.Done()
		setKey := fmt.Sprintf("%s%d", key, i)
		setValue := fmt.Sprintf("value%d", i)
		if err := ring.Set(setKey, setValue, 0); err != nil {
			t.Error(err)
		}
	}
	// set a bunch of keys
	wg.Add(opCount)
	for i := 1; i <= opCount; i++ {
		go setIt(i)
	}
	wg.Wait()
	getIt := func(i int) {
		defer wg.Done()
		getKey := fmt.Sprintf("%s%d", key, i)
		setValue := fmt.Sprintf("value%d", i)
		if getValue, err := ring.Get(getKey); err != nil {
			t.Error(err)
		} else {
			assert.EqualValues(t, setValue, getValue)
		}
	}
	// get a bunch of keys
	wg.Add(opCount)
	for i := 1; i <= opCount; i++ {
		go getIt(i)
	}
	wg.Wait()
}

func testConnectionLimits(t *testing.T, ring *memcacheRing) {
	key := "testConnectionLimits"
	opCount := 1000
	var wg sync.WaitGroup
	setIt := func(i int) {
		defer wg.Done()
		setKey := fmt.Sprintf("%s%d", key, i)
		if err := ring.Set(setKey, "", 0); err != nil {
			t.Error(err)
		}
	}
	// set a bunck of keys
	wg.Add(opCount)
	for i := 0; i < opCount; i++ {
		go setIt(i)
	}
	wg.Wait()
	// verify connections per server are less than the limit
	for _, server := range ring.servers {
		assert.True(t, server.connectionCount() <= 3)
	}
}

func testExpiration(t *testing.T, ring *memcacheRing) {
	if testing.Short() {
		t.Log("Skipping testExpiration()")
		return
	}
	key := "testExpiration"
	opCount := 100
	var wg sync.WaitGroup
	setIt := func(i int, expiration int) {
		defer wg.Done()
		setKey := fmt.Sprintf("%s%d", key, i)
		if err := ring.Set(setKey, "", expiration); err != nil {
			t.Error(err)
		}
	}
	// test both ways of setting expiration
	for i := 0; i < 2; i++ {
		var expiration int
		if i == 0 {
			expiration = 1
		} else {
			now := time.Now()
			expiration = int(now.Unix()) + 1
		}
		wg.Add(opCount)
		// set a bunch of keys with expiration
		for i := 0; i < opCount; i++ {
			go setIt(i, expiration)
		}
		wg.Wait()
		// sleep so the items will expire
		time.Sleep(2 * time.Second)
		// get a bunch of keys and verify cache miss
		for i := 0; i < opCount; i++ {
			getKey := fmt.Sprintf("%s%d", key, i)
			if _, err := ring.Get(getKey); err != nil {
				assert.EqualValues(t, CacheMiss, err)
			} else {
				t.Errorf("Expected a cache miss")
			}
		}
	}
}
<|endoftext|>


<commit_before>// 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 spinner is a simple package to add a spinner/progress indicator to your application.
package spinner

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// CharSets contains the available character sets
var CharSets = [][]string{
	{"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
	{"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
	{"▖", "▘", "▝", "▗"},
	{"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
	{"◢", "◣", "◤", "◥"},
	{"◰", "◳", "◲", "◱"},
	{"◴", "◷", "◶", "◵"},
	{"◐", "◓", "◑", "◒"},
	{".", "o", "O", "@", "*"},
	{"|", "/", "-", "\\"},
	{"◡◡", "⊙⊙", "◠◠"},
	{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
	{">))'>", " >))'>", "  >))'>", "   >))'>", "    >))'>", "   <'((<", "  <'((<", " <'((<"},
	{"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
	{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
	{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
	{"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
	{"■", "□", "▪", "▫"},
	{"←", "↑", "→", "↓"},
	{"╫", "╪"},
	{"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
	{"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
	{"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
	{"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
	{"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
	{"ｦ", "ｧ", "ｨ", "ｩ", "ｪ", "ｫ", "ｬ", "ｭ", "ｮ", "ｯ", "ｱ", "ｲ", "ｳ", "ｴ", "ｵ", "ｶ", "ｷ", "ｸ", "ｹ", "ｺ", "ｻ", "ｼ", "ｽ", "ｾ", "ｿ", "ﾀ", "ﾁ", "ﾂ", "ﾃ", "ﾄ", "ﾅ", "ﾆ", "ﾇ", "ﾈ", "ﾉ", "ﾊ", "ﾋ", "ﾌ", "ﾍ", "ﾎ", "ﾏ", "ﾐ", "ﾑ", "ﾒ", "ﾓ", "ﾔ", "ﾕ", "ﾖ", "ﾗ", "ﾘ", "ﾙ", "ﾚ", "ﾛ", "ﾜ", "ﾝ"},
	{".", "..", "..."},
	{"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
	{".", "o", "O", "°", "O", "o", "."},
	{"+", "x"},
	{"v", "<", "^", ">"},
	{">>--->", " >>--->", "  >>--->", "   >>--->", "    >>--->", "    <---<<", "   <---<<", "  <---<<", " <---<<", "<---<<"},
	{"|", "||", "|||", "||||", "|||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
	{"[          ]", "[=         ]", "[==        ]", "[===       ]", "[====      ]", "[=====     ]", "[======    ]", "[=======   ]", "[========  ]", "[========= ]", "[==========]"},
	{"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
	{"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
	{"[                    ]", "[=>                  ]", "[===>                ]", "[=====>              ]", "[======>             ]", "[========>           ]", "[==========>         ]", "[============>       ]", "[==============>     ]", "[================>   ]", "[==================> ]", "[===================>]"},
}

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// state is a type for the spinner status
type state uint8

// Holds a copy of the Spinner config for each new goroutine
type spinningConfig struct {
	chars      []string
	delay      time.Duration
	prefix     string
	suffix     string
	color      func(a ...interface{}) string
	lastOutput string
}

// Spinner struct to hold the provided options
type Spinner struct {
	chars          []string                      // chars holds the chosen character set
	Delay          time.Duration                 // Delay is the speed of the spinner
	Prefix         string                        // Prefix is the text preppended to the spinner
	Suffix         string                        // Suffix is the text appended to the spinner
	stopChan       chan struct{}                 // stopChan is a channel used to stop the spinner
	ST             state                         // spinner status
	Writer         io.Writer                     // to make testing better
	color          func(a ...interface{}) string // default color is white
	lastOutput     string                        // last character(set) written
	lastOutputChan chan string                   // allows main to safely get the last output from the spinner goroutine
	Finally        string                        // string displayed after Stop() is called
}

//go:generate stringer -type=state
const (
	stopped state = iota
	running
)

// validColors holds an array of the only colors allowed
var validColors = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	for _, i := range validColors {
		if c == i {
			return true
		}
	}
	return false
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(c []string, t time.Duration) *Spinner {
	s := &Spinner{
		Delay:          t,
		stopChan:       make(chan struct{}, 1),
		lastOutputChan: make(chan string, 1),
		color:          color.New(color.FgWhite).SprintFunc(),
		Writer:         color.Output,
	}
	s.UpdateCharSet(c)
	return s
}

// Start will start the spinner
func (s *Spinner) Start() {
	if s.ST == running {
		return
	}
	s.ST = running

	// Create a copy of the Spinner config for use by the spinning
	// goroutine to avoid races between accesses by main and the goroutine.
	cfg := &spinningConfig{
		chars:      make([]string, len(s.chars)),
		delay:      s.Delay,
		prefix:     s.Prefix,
		suffix:     s.Suffix,
		color:      s.color,
		lastOutput: s.lastOutput,
	}
	copy(cfg.chars, s.chars)

	go func(c *spinningConfig) {
		for {
			for i := 0; i < len(c.chars); i++ {
				select {
				case <-s.stopChan:
					erase(s.Writer, c.lastOutput)
					s.lastOutputChan <- c.lastOutput
					return
				default:
					fmt.Fprint(s.Writer, fmt.Sprintf("%s%s%s ", c.prefix, c.color(c.chars[i]), c.suffix))
					out := fmt.Sprintf("%s%s%s ", c.prefix, c.chars[i], c.suffix)
					c.lastOutput = out
					time.Sleep(c.delay)
					erase(s.Writer, out)
				}
			}
		}
	}(cfg)
}

// erase deletes written characters
func erase(w io.Writer, a string) {
	n := utf8.RuneCountInString(a)
	for i := 0; i < n; i++ {
		fmt.Fprintf(w, "\b")
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(c string) error {
	if validColor(c) {
		switch c {
		case "red":
			s.color = color.New(color.FgRed).SprintFunc()
			s.Restart()
		case "yellow":
			s.color = color.New(color.FgYellow).SprintFunc()
			s.Restart()
		case "green":
			s.color = color.New(color.FgGreen).SprintFunc()
			s.Restart()
		case "magenta":
			s.color = color.New(color.FgMagenta).SprintFunc()
			s.Restart()
		case "blue":
			s.color = color.New(color.FgBlue).SprintFunc()
			s.Restart()
		case "cyan":
			s.color = color.New(color.FgCyan).SprintFunc()
			s.Restart()
		case "white":
			s.color = color.New(color.FgWhite).SprintFunc()
			s.Restart()
		default:
			return errInvalidColor
		}
	}
	return nil
}

// Stop stops the spinner
func (s *Spinner) Stop() {
	if s.ST == running {
		s.stopChan <- struct{}{}
		s.ST = stopped
		s.lastOutput = <-s.lastOutputChan
		if s.Finally != "" {
			fmt.Fprintf(s.Writer, s.Finally)
		}
	}
}

// Restart will stop and start the spinner
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to that spinner
func (s *Spinner) Reverse() {
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// UpdateSpeed will set the spinner delay to the given value
func (s *Spinner) UpdateSpeed(delay time.Duration) { s.Delay = delay }

// UpdateCharSet will change the current charSet to the given one
func (s *Spinner) UpdateCharSet(chars []string) {
	// so that changes to the slice outside of the spinner don't change it
	// unexpectedly, create an internal copy
	n := make([]string, len(chars))
	copy(n, chars)
	s.chars = n
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	//numSeq := make([]string, 0)
	var numSeq []string
	for i := 0; i < length; i++ {
		numSeq = append(numSeq, strconv.Itoa(i))
	}
	return numSeq
}
<commit_msg>change field name to be clearer<commit_after>// 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 spinner is a simple package to add a spinner/progress indicator to your application.
package spinner

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// CharSets contains the available character sets
var CharSets = [][]string{
	{"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
	{"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
	{"▖", "▘", "▝", "▗"},
	{"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
	{"◢", "◣", "◤", "◥"},
	{"◰", "◳", "◲", "◱"},
	{"◴", "◷", "◶", "◵"},
	{"◐", "◓", "◑", "◒"},
	{".", "o", "O", "@", "*"},
	{"|", "/", "-", "\\"},
	{"◡◡", "⊙⊙", "◠◠"},
	{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
	{">))'>", " >))'>", "  >))'>", "   >))'>", "    >))'>", "   <'((<", "  <'((<", " <'((<"},
	{"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
	{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
	{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
	{"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
	{"■", "□", "▪", "▫"},
	{"←", "↑", "→", "↓"},
	{"╫", "╪"},
	{"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
	{"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
	{"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
	{"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
	{"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
	{"ｦ", "ｧ", "ｨ", "ｩ", "ｪ", "ｫ", "ｬ", "ｭ", "ｮ", "ｯ", "ｱ", "ｲ", "ｳ", "ｴ", "ｵ", "ｶ", "ｷ", "ｸ", "ｹ", "ｺ", "ｻ", "ｼ", "ｽ", "ｾ", "ｿ", "ﾀ", "ﾁ", "ﾂ", "ﾃ", "ﾄ", "ﾅ", "ﾆ", "ﾇ", "ﾈ", "ﾉ", "ﾊ", "ﾋ", "ﾌ", "ﾍ", "ﾎ", "ﾏ", "ﾐ", "ﾑ", "ﾒ", "ﾓ", "ﾔ", "ﾕ", "ﾖ", "ﾗ", "ﾘ", "ﾙ", "ﾚ", "ﾛ", "ﾜ", "ﾝ"},
	{".", "..", "..."},
	{"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
	{".", "o", "O", "°", "O", "o", "."},
	{"+", "x"},
	{"v", "<", "^", ">"},
	{">>--->", " >>--->", "  >>--->", "   >>--->", "    >>--->", "    <---<<", "   <---<<", "  <---<<", " <---<<", "<---<<"},
	{"|", "||", "|||", "||||", "|||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
	{"[          ]", "[=         ]", "[==        ]", "[===       ]", "[====      ]", "[=====     ]", "[======    ]", "[=======   ]", "[========  ]", "[========= ]", "[==========]"},
	{"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
	{"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
	{"[                    ]", "[=>                  ]", "[===>                ]", "[=====>              ]", "[======>             ]", "[========>           ]", "[==========>         ]", "[============>       ]", "[==============>     ]", "[================>   ]", "[==================> ]", "[===================>]"},
}

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// state is a type for the spinner status
type state uint8

// Holds a copy of the Spinner config for each new goroutine
type spinningConfig struct {
	chars      []string
	delay      time.Duration
	prefix     string
	suffix     string
	color      func(a ...interface{}) string
	lastOutput string
}

// Spinner struct to hold the provided options
type Spinner struct {
	chars          []string                      // chars holds the chosen character set
	Delay          time.Duration                 // Delay is the speed of the spinner
	Prefix         string                        // Prefix is the text preppended to the spinner
	Suffix         string                        // Suffix is the text appended to the spinner
	stopChan       chan struct{}                 // stopChan is a channel used to stop the spinner
	ST             state                         // spinner status
	Writer         io.Writer                     // to make testing better
	color          func(a ...interface{}) string // default color is white
	lastOutput     string                        // last character(set) written
	lastOutputChan chan string                   // allows main to safely get the last output from the spinner goroutine
	FinalMSG       string                        // string displayed after Stop() is called
}

//go:generate stringer -type=state
const (
	stopped state = iota
	running
)

// validColors holds an array of the only colors allowed
var validColors = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	for _, i := range validColors {
		if c == i {
			return true
		}
	}
	return false
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(c []string, t time.Duration) *Spinner {
	s := &Spinner{
		Delay:          t,
		stopChan:       make(chan struct{}, 1),
		lastOutputChan: make(chan string, 1),
		color:          color.New(color.FgWhite).SprintFunc(),
		Writer:         color.Output,
	}
	s.UpdateCharSet(c)
	return s
}

// Start will start the spinner
func (s *Spinner) Start() {
	if s.ST == running {
		return
	}
	s.ST = running

	// Create a copy of the Spinner config for use by the spinning
	// goroutine to avoid races between accesses by main and the goroutine.
	cfg := &spinningConfig{
		chars:      make([]string, len(s.chars)),
		delay:      s.Delay,
		prefix:     s.Prefix,
		suffix:     s.Suffix,
		color:      s.color,
		lastOutput: s.lastOutput,
	}
	copy(cfg.chars, s.chars)

	go func(c *spinningConfig) {
		for {
			for i := 0; i < len(c.chars); i++ {
				select {
				case <-s.stopChan:
					erase(s.Writer, c.lastOutput)
					s.lastOutputChan <- c.lastOutput
					return
				default:
					fmt.Fprint(s.Writer, fmt.Sprintf("%s%s%s ", c.prefix, c.color(c.chars[i]), c.suffix))
					out := fmt.Sprintf("%s%s%s ", c.prefix, c.chars[i], c.suffix)
					c.lastOutput = out
					time.Sleep(c.delay)
					erase(s.Writer, out)
				}
			}
		}
	}(cfg)
}

// erase deletes written characters
func erase(w io.Writer, a string) {
	n := utf8.RuneCountInString(a)
	for i := 0; i < n; i++ {
		fmt.Fprintf(w, "\b")
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(c string) error {
	if validColor(c) {
		switch c {
		case "red":
			s.color = color.New(color.FgRed).SprintFunc()
			s.Restart()
		case "yellow":
			s.color = color.New(color.FgYellow).SprintFunc()
			s.Restart()
		case "green":
			s.color = color.New(color.FgGreen).SprintFunc()
			s.Restart()
		case "magenta":
			s.color = color.New(color.FgMagenta).SprintFunc()
			s.Restart()
		case "blue":
			s.color = color.New(color.FgBlue).SprintFunc()
			s.Restart()
		case "cyan":
			s.color = color.New(color.FgCyan).SprintFunc()
			s.Restart()
		case "white":
			s.color = color.New(color.FgWhite).SprintFunc()
			s.Restart()
		default:
			return errInvalidColor
		}
	}
	return nil
}

// Stop stops the spinner
func (s *Spinner) Stop() {
	if s.ST == running {
		s.stopChan <- struct{}{}
		s.ST = stopped
		s.lastOutput = <-s.lastOutputChan
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
	}
}

// Restart will stop and start the spinner
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to that spinner
func (s *Spinner) Reverse() {
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// UpdateSpeed will set the spinner delay to the given value
func (s *Spinner) UpdateSpeed(delay time.Duration) { s.Delay = delay }

// UpdateCharSet will change the current charSet to the given one
func (s *Spinner) UpdateCharSet(chars []string) {
	// so that changes to the slice outside of the spinner don't change it
	// unexpectedly, create an internal copy
	n := make([]string, len(chars))
	copy(n, chars)
	s.chars = n
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	//numSeq := make([]string, 0)
	var numSeq []string
	for i := 0; i < length; i++ {
		numSeq = append(numSeq, strconv.Itoa(i))
	}
	return numSeq
}
<|endoftext|>


<commit_before>package pop

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	// Load MySQL Go driver
	_ "github.com/go-sql-driver/mysql"
	"github.com/gobuffalo/pop/columns"
	"github.com/gobuffalo/pop/fizz"
	"github.com/gobuffalo/pop/fizz/translators"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
)

var _ dialect = &mysql{}

type mysql struct {
	ConnectionDetails *ConnectionDetails
}

func (m *mysql) Name() string {
	return "mysql"
}

func (m *mysql) Details() *ConnectionDetails {
	return m.ConnectionDetails
}

func (m *mysql) URL() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		return strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
	}
	s := "%s:%s@(%s:%s)/%s?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port, c.Database)
}

func (m *mysql) urlWithoutDb() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		// respect user's own URL definition (with options).
		url := strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
		return strings.Replace(url, "/"+c.Database+"?", "/?", 1)
	}
	s := "%s:%s@(%s:%s)/?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port)
}

func (m *mysql) MigrationURL() string {
	return m.URL()
}

func (m *mysql) Create(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericCreate(s, model, cols), "mysql create")
}

func (m *mysql) Update(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericUpdate(s, model, cols), "mysql update")
}

func (m *mysql) Destroy(s store, model *Model) error {
	return errors.Wrap(genericDestroy(s, model), "mysql destroy")
}

func (m *mysql) SelectOne(s store, model *Model, query Query) error {
	return errors.Wrap(genericSelectOne(s, model, query), "mysql select one")
}

func (m *mysql) SelectMany(s store, models *Model, query Query) error {
	return errors.Wrap(genericSelectMany(s, models, query), "mysql select many")
}

// CreateDB creates a new database, from the given connection credentials
func (m *mysql) CreateDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("CREATE DATABASE `%s` DEFAULT COLLATE `utf8_general_ci`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}

	fmt.Printf("created database %s\n", deets.Database)
	return nil
}

// DropDB drops an existing database, from the given connection credentials
func (m *mysql) DropDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("DROP DATABASE `%s`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}

	fmt.Printf("dropped database %s\n", deets.Database)
	return nil
}

func (m *mysql) TranslateSQL(sql string) string {
	return sql
}

func (m *mysql) FizzTranslator() fizz.Translator {
	t := translators.NewMySQL(m.URL(), m.Details().Database)
	return t
}

func (m *mysql) Lock(fn func() error) error {
	return fn()
}

func (m *mysql) DumpSchema(w io.Writer) error {
	deets := m.Details()
	cmd := exec.Command("mysqldump", "-d", "-h", deets.Host, "-P", deets.Port, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysqldump", "-d", "-S", deets.Host, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	}
	Log(strings.Join(cmd.Args, " "))
	cmd.Stdout = w
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if err != nil {
		return err
	}

	fmt.Printf("dumped schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) LoadSchema(r io.Reader) error {
	deets := m.Details()
	cmd := exec.Command("mysql", "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), "-h", deets.Host, "-P", deets.Port, "-D", deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysql", "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), "-S", deets.Host, "-D", deets.Database)
	}
	in, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	go func() {
		defer in.Close()
		io.Copy(in, r)
	}()
	Log(strings.Join(cmd.Args, " "))
	err = cmd.Start()
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return err
	}

	fmt.Printf("loaded schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) TruncateAll(tx *Connection) error {
	stmts := []struct {
		Stmt string `db:"stmt"`
	}{}
	err := tx.RawQuery(mysqlTruncate, m.Details().Database).All(&stmts)
	if err != nil {
		return err
	}
	if len(stmts) == 0 {
		return nil
	}
	qs := []string{}
	for _, x := range stmts {
		qs = append(qs, x.Stmt)
	}
	return tx.RawQuery(strings.Join(qs, " ")).Exec()
}

func newMySQL(deets *ConnectionDetails) dialect {
	cd := &mysql{
		ConnectionDetails: deets,
	}

	return cd
}

const mysqlTruncate = "SELECT concat('TRUNCATE TABLE `', TABLE_NAME, '`;') as stmt FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = ?"
<commit_msg>Fix #49: disable MySQL foreign keys on truncate all (#88)<commit_after>package pop

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	// Load MySQL Go driver
	_ "github.com/go-sql-driver/mysql"
	"github.com/gobuffalo/pop/columns"
	"github.com/gobuffalo/pop/fizz"
	"github.com/gobuffalo/pop/fizz/translators"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
)

var _ dialect = &mysql{}

type mysql struct {
	ConnectionDetails *ConnectionDetails
}

func (m *mysql) Name() string {
	return "mysql"
}

func (m *mysql) Details() *ConnectionDetails {
	return m.ConnectionDetails
}

func (m *mysql) URL() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		return strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
	}
	s := "%s:%s@(%s:%s)/%s?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port, c.Database)
}

func (m *mysql) urlWithoutDb() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		// respect user's own URL definition (with options).
		url := strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
		return strings.Replace(url, "/"+c.Database+"?", "/?", 1)
	}
	s := "%s:%s@(%s:%s)/?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port)
}

func (m *mysql) MigrationURL() string {
	return m.URL()
}

func (m *mysql) Create(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericCreate(s, model, cols), "mysql create")
}

func (m *mysql) Update(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericUpdate(s, model, cols), "mysql update")
}

func (m *mysql) Destroy(s store, model *Model) error {
	return errors.Wrap(genericDestroy(s, model), "mysql destroy")
}

func (m *mysql) SelectOne(s store, model *Model, query Query) error {
	return errors.Wrap(genericSelectOne(s, model, query), "mysql select one")
}

func (m *mysql) SelectMany(s store, models *Model, query Query) error {
	return errors.Wrap(genericSelectMany(s, models, query), "mysql select many")
}

// CreateDB creates a new database, from the given connection credentials
func (m *mysql) CreateDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("CREATE DATABASE `%s` DEFAULT COLLATE `utf8_general_ci`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}

	fmt.Printf("created database %s\n", deets.Database)
	return nil
}

// DropDB drops an existing database, from the given connection credentials
func (m *mysql) DropDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("DROP DATABASE `%s`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}

	fmt.Printf("dropped database %s\n", deets.Database)
	return nil
}

func (m *mysql) TranslateSQL(sql string) string {
	return sql
}

func (m *mysql) FizzTranslator() fizz.Translator {
	t := translators.NewMySQL(m.URL(), m.Details().Database)
	return t
}

func (m *mysql) Lock(fn func() error) error {
	return fn()
}

func (m *mysql) DumpSchema(w io.Writer) error {
	deets := m.Details()
	cmd := exec.Command("mysqldump", "-d", "-h", deets.Host, "-P", deets.Port, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysqldump", "-d", "-S", deets.Host, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	}
	Log(strings.Join(cmd.Args, " "))
	cmd.Stdout = w
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if err != nil {
		return err
	}

	fmt.Printf("dumped schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) LoadSchema(r io.Reader) error {
	deets := m.Details()
	cmd := exec.Command("mysql", "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), "-h", deets.Host, "-P", deets.Port, "-D", deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysql", "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), "-S", deets.Host, "-D", deets.Database)
	}
	in, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	go func() {
		defer in.Close()
		io.Copy(in, r)
	}()
	Log(strings.Join(cmd.Args, " "))
	err = cmd.Start()
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return err
	}

	fmt.Printf("loaded schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) TruncateAll(tx *Connection) error {
	stmts := []string{}
	err := tx.RawQuery(mysqlTruncate, m.Details().Database).All(&stmts)
	if err != nil {
		return err
	}
	if len(stmts) == 0 {
		return nil
	}

	var qb bytes.Buffer
	// #49: Disable foreign keys before truncation
	qb.WriteString("SET SESSION FOREIGN_KEY_CHECKS = 0; ")
	qb.WriteString(strings.Join(stmts, " "))
	// #49: Re-enable foreign keys after truncation
	qb.WriteString(" SET SESSION FOREIGN_KEY_CHECKS = 1;")

	return tx.RawQuery(qb.String()).Exec()
}

func newMySQL(deets *ConnectionDetails) dialect {
	cd := &mysql{
		ConnectionDetails: deets,
	}

	return cd
}

const mysqlTruncate = "SELECT concat('TRUNCATE TABLE `', TABLE_NAME, '`;') as stmt FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_type <> 'VIEW'"
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package events_test

import (
	"fmt"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp/cmpopts"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	cloudevents "github.com/cloudevents/sdk-go"
	"github.com/google/go-cmp/cmp"
	"knative.dev/eventing/pkg/adapter/apiserver/events"
)

var contentType = "application/json"

func simplePod(name, namespace string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Pod",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
		},
	}
}

func simpleSubject(name, namespace string) *string {
	subject := fmt.Sprintf("/apis/v1/namespaces/%s/pods/%s", namespace, name)
	return &subject
}

func simpleOwnedPod(name, namespace string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Pod",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      "owned",
				"ownerReferences": []interface{}{
					map[string]interface{}{
						"apiVersion":         "apps/v1",
						"blockOwnerDeletion": true,
						"controller":         true,
						"kind":               "ReplicaSet",
						"name":               name,
						"uid":                "0c119059-7113-11e9-a6c5-42010a8a00ed",
					},
				},
			},
		},
	}
}

func TestMakeAddEvent(t *testing.T) {
	testCases := map[string]struct {
		obj    interface{}
		source string

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.resource.add",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"apiVersion":"v1","kind":"Pod","metadata":{"name":"unit","namespace":"test"}}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeAddEvent(tc.source, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeUpdateEvent(t *testing.T) {
	testCases := map[string]struct {
		obj    interface{}
		source string

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "new resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.resource.update",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"apiVersion":"v1","kind":"Pod","metadata":{"name":"unit","namespace":"test"}}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeUpdateEvent(tc.source, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeDeleteEvent(t *testing.T) {
	testCases := map[string]struct {
		obj    interface{}
		source string

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.resource.delete",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"apiVersion":"v1","kind":"Pod","metadata":{"name":"unit","namespace":"test"}}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeDeleteEvent(tc.source, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeAddRefEvent(t *testing.T) {
	testCases := map[string]struct {
		obj          interface{}
		source       string
		asController bool

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.ref.add",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"Pod","namespace":"test","name":"unit","apiVersion":"v1"}`,
		},
		"simple owned pod": {
			source:       "unit-test",
			obj:          simpleOwnedPod("unit", "test"),
			asController: true,
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.ref.add",
					DataContentType: &contentType,
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("owned", "test"),
				}.AsV1(),
			},
			wantData: `{"kind":"ReplicaSet","namespace":"test","name":"unit","apiVersion":"apps/v1"}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeAddRefEvent(tc.source, tc.asController, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeUpdateRefEvent(t *testing.T) {
	testCases := map[string]struct {
		obj          interface{}
		source       string
		asController bool

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "new resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.ref.update",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"Pod","namespace":"test","name":"unit","apiVersion":"v1"}`,
		},
		"simple owned pod": {
			source:       "unit-test",
			obj:          simpleOwnedPod("unit", "test"),
			asController: true,
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.ref.update",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("owned", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"ReplicaSet","namespace":"test","name":"unit","apiVersion":"apps/v1"}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeUpdateRefEvent(tc.source, tc.asController, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeDeleteRefEvent(t *testing.T) {
	testCases := map[string]struct {
		obj          interface{}
		source       string
		asController bool

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.ref.delete",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"Pod","namespace":"test","name":"unit","apiVersion":"v1"}`,
		},
		"simple owned pod": {
			source:       "unit-test",
			obj:          simpleOwnedPod("unit", "test"),
			asController: true,
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:        "dev.knative.apiserver.ref.delete",
					Source:      *cloudevents.ParseURIRef("unit-test"),
					Subject:     simpleSubject("owned", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"ReplicaSet","namespace":"test","name":"unit","apiVersion":"apps/v1"}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeDeleteRefEvent(tc.source, tc.asController, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func validate(t *testing.T, got *cloudevents.Event, err error, want *cloudevents.Event, wantData, wantErr string) {
	if wantErr != "" || err != nil {
		var gotErr string
		if err != nil {
			gotErr = err.Error()
		}
		if !strings.Contains(wantErr, gotErr) {
			diff := cmp.Diff(wantErr, gotErr)
			t.Errorf("unexpected error (-want, +got) = %v", diff)
		}
		return
	}

	if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(cloudevents.Event{}, "Data", "DataEncoded")); diff != "" {
		t.Errorf("unexpected event diff (-want, +got) = %v", diff)
	}

	gotData := string(got.Data.([]byte))
	if diff := cmp.Diff(wantData, gotData); diff != "" {
		t.Errorf("unexpected data diff (-want, +got) = %v", diff)
	}
}
<commit_msg>golang format tools (#2262)<commit_after>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package events_test

import (
	"fmt"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp/cmpopts"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	cloudevents "github.com/cloudevents/sdk-go"
	"github.com/google/go-cmp/cmp"
	"knative.dev/eventing/pkg/adapter/apiserver/events"
)

var contentType = "application/json"

func simplePod(name, namespace string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Pod",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
		},
	}
}

func simpleSubject(name, namespace string) *string {
	subject := fmt.Sprintf("/apis/v1/namespaces/%s/pods/%s", namespace, name)
	return &subject
}

func simpleOwnedPod(name, namespace string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Pod",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      "owned",
				"ownerReferences": []interface{}{
					map[string]interface{}{
						"apiVersion":         "apps/v1",
						"blockOwnerDeletion": true,
						"controller":         true,
						"kind":               "ReplicaSet",
						"name":               name,
						"uid":                "0c119059-7113-11e9-a6c5-42010a8a00ed",
					},
				},
			},
		},
	}
}

func TestMakeAddEvent(t *testing.T) {
	testCases := map[string]struct {
		obj    interface{}
		source string

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.resource.add",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"apiVersion":"v1","kind":"Pod","metadata":{"name":"unit","namespace":"test"}}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeAddEvent(tc.source, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeUpdateEvent(t *testing.T) {
	testCases := map[string]struct {
		obj    interface{}
		source string

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "new resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.resource.update",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"apiVersion":"v1","kind":"Pod","metadata":{"name":"unit","namespace":"test"}}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeUpdateEvent(tc.source, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeDeleteEvent(t *testing.T) {
	testCases := map[string]struct {
		obj    interface{}
		source string

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.resource.delete",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"apiVersion":"v1","kind":"Pod","metadata":{"name":"unit","namespace":"test"}}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeDeleteEvent(tc.source, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeAddRefEvent(t *testing.T) {
	testCases := map[string]struct {
		obj          interface{}
		source       string
		asController bool

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.ref.add",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"Pod","namespace":"test","name":"unit","apiVersion":"v1"}`,
		},
		"simple owned pod": {
			source:       "unit-test",
			obj:          simpleOwnedPod("unit", "test"),
			asController: true,
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.ref.add",
					DataContentType: &contentType,
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("owned", "test"),
				}.AsV1(),
			},
			wantData: `{"kind":"ReplicaSet","namespace":"test","name":"unit","apiVersion":"apps/v1"}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeAddRefEvent(tc.source, tc.asController, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeUpdateRefEvent(t *testing.T) {
	testCases := map[string]struct {
		obj          interface{}
		source       string
		asController bool

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "new resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.ref.update",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"Pod","namespace":"test","name":"unit","apiVersion":"v1"}`,
		},
		"simple owned pod": {
			source:       "unit-test",
			obj:          simpleOwnedPod("unit", "test"),
			asController: true,
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.ref.update",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("owned", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"ReplicaSet","namespace":"test","name":"unit","apiVersion":"apps/v1"}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeUpdateRefEvent(tc.source, tc.asController, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func TestMakeDeleteRefEvent(t *testing.T) {
	testCases := map[string]struct {
		obj          interface{}
		source       string
		asController bool

		want     *cloudevents.Event
		wantData string
		wantErr  string
	}{
		"nil object": {
			source:  "unit-test",
			want:    nil,
			wantErr: "resource can not be nil",
		},
		"simple pod": {
			source: "unit-test",
			obj:    simplePod("unit", "test"),
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.ref.delete",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("unit", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"Pod","namespace":"test","name":"unit","apiVersion":"v1"}`,
		},
		"simple owned pod": {
			source:       "unit-test",
			obj:          simpleOwnedPod("unit", "test"),
			asController: true,
			want: &cloudevents.Event{
				Context: cloudevents.EventContextV1{
					Type:            "dev.knative.apiserver.ref.delete",
					Source:          *cloudevents.ParseURIRef("unit-test"),
					Subject:         simpleSubject("owned", "test"),
					DataContentType: &contentType,
				}.AsV1(),
			},
			wantData: `{"kind":"ReplicaSet","namespace":"test","name":"unit","apiVersion":"apps/v1"}`,
		},
	}
	for n, tc := range testCases {
		t.Run(n, func(t *testing.T) {
			got, err := events.MakeDeleteRefEvent(tc.source, tc.asController, tc.obj)
			validate(t, got, err, tc.want, tc.wantData, tc.wantErr)
		})
	}
}

func validate(t *testing.T, got *cloudevents.Event, err error, want *cloudevents.Event, wantData, wantErr string) {
	if wantErr != "" || err != nil {
		var gotErr string
		if err != nil {
			gotErr = err.Error()
		}
		if !strings.Contains(wantErr, gotErr) {
			diff := cmp.Diff(wantErr, gotErr)
			t.Errorf("unexpected error (-want, +got) = %v", diff)
		}
		return
	}

	if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(cloudevents.Event{}, "Data", "DataEncoded")); diff != "" {
		t.Errorf("unexpected event diff (-want, +got) = %v", diff)
	}

	gotData := string(got.Data.([]byte))
	if diff := cmp.Diff(wantData, gotData); diff != "" {
		t.Errorf("unexpected data diff (-want, +got) = %v", diff)
	}
}
<|endoftext|>