<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"net/http"
	"net/http/httputil"
	"net/url"
	// "io"
	"io/ioutil"
	"flag"
	"time"
	"strings"
)

func get(hostname string, port int, path string, auth string, urls bool, verbose bool, timeout int) (rv bool, err error) {

	// defer func() {
	// 	if err := recover(); err != nil {
	// 		return
	// 	}
	// }()

	rv = true

	if verbose {
		fmt.Fprintf(os.Stderr, "fetching:hostname:%s:\n", hostname)
	}

	res := &http.Response{}

	if urls {

		url := hostname
		res, err = http.Head(url)
		defer res.Body.Close()

		// req, err = http.NewRequest("HEAD", url, nil)

		if err != nil {
			fmt.Println(err.Error())
			rv = false
			return
		}

	} else {

		client := &http.Client{Timeout: time.Duration(timeout) * time.Second}

		// had to allocate this or the SetBasicAuth will panic
	    headers := make(map[string][]string)
	    hostPort := fmt.Sprintf("%s:%d", hostname, port)

	    if verbose {

		    fmt.Fprintf(os.Stderr, "adding hostPort:%s:%d:path:%s:\n", hostname, port, path)

	    }
		req := &http.Request{
			Method: "HEAD",
			// Host:  hostPort,
			URL: &url.URL{
				Host:   hostPort,
				Scheme: "http",
				Opaque: path,
			},
			Header: headers,
		}

	    if auth != "" {

	    	up := strings.SplitN(auth, ":", 2)

	    	if verbose {

			    fmt.Fprintf(os.Stderr, "Doing auth with:username:%s:password:%s:", up[0], up[1])

	    	}
			req.SetBasicAuth(up[0], up[1])

	    }

	    if verbose {

		    dump, _ := httputil.DumpRequestOut(req, true)
		    fmt.Fprintf(os.Stderr, "%s", dump)

	    }

		res, err = client.Do(req)
		if err != nil {
			fmt.Println(err.Error())
			rv = false
			return
		}

		defer res.Body.Close()
		_, err = ioutil.ReadAll(res.Body)

	}

	// res, err := http.Head(url)
	// if err != nil {
	// 	fmt.Println(err.Error())
	// 	rv = false
	// 	return
	// }

	if verbose {

		fmt.Println(res.Status)
		for k, v := range res.Header {
			fmt.Println(k+":", v)
		}

	}

	if res.StatusCode != http.StatusOK {
		rv = false
	}

	return
}

func main() {

	status := "OK"
	rv := 0
	name := "Bulk HTTP"
	bad := 0
	total := 0

	// this needs improvement. the number of spaces here has to equal the number of chars in the badHosts append line suffix
	badHosts := []byte("  ")

	verbose := flag.Bool("v", false, "verbose output")
	warn := flag.Int("w", 10, "warning level - number of non-200s or percentage of non-200s (default is numeric not percentage)")
	crit := flag.Int("c", 20, "critical level - number of non-200s or percentage of non-200s (default is numeric not percentage)")
	timeout := flag.Int("t", 2, "timeout in seconds - don't wait.  Do Head requests and don't wait.")
	// pct := flag.Bool("pct", false, "interpret warming and critical levels are percentages")
	path := flag.String("path", "", "optional path to append to the stdin lines - these will not be urlencoded. This is ignored is the urls option is given (not implemented yet).")
	file := flag.String("file", "", "optional path to read data from a file instead of stdin.  If its a dash then read from stdin - these will not be urlencoded")
	port := flag.Int("port", 80, "optional port for the http request - ignored if urls is specified")
	urls := flag.Bool("urls", false, "Assume the input data is full urls - its normally a list of hostnames")
	auth := flag.String("auth", "", "Do basic auth with this username:passwd - ignored if urls is specified - make this use .netrc instead")

	flag.Usage = func() {

        fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
        fmt.Fprintf(os.Stderr, `
	Read hostnames from a file or STDIN and do a single nagios check over
	them all.  Just check for 200s.  Warning and Critical are either
	percentages of the total, or a regular numeric thresholds.

	The output contains the hostname of any non-200 reporting hosts.

	Skip input lines that are commented out with shell style comments
	like /^#/.

	Do Head requests since we don't care about the content.  Make this
	optional some day.

	Also make this read from a file of urls with a -f option.

	Make the auth configurable from the cli

    	`)

        flag.PrintDefaults()
	}

	flag.Parse()

	if len(flag.Args()) > 0 {

		flag.Usage()
		os.Exit(3)

	}

	// it urls is specified, the input is full urls to be used enmasse and to be url encoded
	if *urls {
		*path = ""
	}

	// defer func() {
	// 	if err := recover(); err != nil {
	// 		fmt.Println(name+" Unknown: ", err)
	// 		os.Exit(3)
	// 	}
	// }()

	if file == nil || *file == "" {
		flag.Usage()
		os.Exit(3)
	}

	// inputSource := &io.Reader
	inputSource := os.Stdin

	if (*file)[0] != "-"[0] {

		var err error

		inputSource, err = os.Open(*file)

		if err != nil {

			fmt.Printf("Couldn't open the specified input file:%s:error:%v:\n\n", name, err)
			flag.Usage()
			os.Exit(3)

		}

	}

	scanner := bufio.NewScanner(inputSource)
	for scanner.Scan() {

		total++

		hostname := scanner.Text()

		if hostname[0] == "#"[0] {

			if *verbose {

				fmt.Printf("skipping:%s:\n", hostname)

			}

			continue
		}

		if *verbose {

			fmt.Printf("working on:%s:\n", hostname)

		}

		goodCheck, err := get(hostname, *port, *path, *auth, *urls, *verbose, *timeout)
		if err != nil {

			fmt.Printf("%s get error: %T %s %#v\n", name, err, err, err)

			continue

		}

		if !goodCheck {
			badHosts = append(badHosts, hostname...)
			badHosts = append(badHosts, ", "...)
			bad++
		}

	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}

	if bad >= *crit {
		status = "Critical"
		rv = 1
	} else if bad >= *warn {
		status = "Warning"
		rv = 2
	}

	fmt.Printf("%s %s: %d |%s\n", name, status, bad, badHosts[:len(badHosts) - 2] )
	os.Exit(rv)
}
<commit_msg>go fmt<commit_after>package main

import (
	"bufio"
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	// "io"
	"flag"
	"io/ioutil"
	"strings"
	"time"
)

func get(hostname string, port int, path string, auth string, urls bool, verbose bool, timeout int) (rv bool, err error) {

	// defer func() {
	// 	if err := recover(); err != nil {
	// 		return
	// 	}
	// }()

	rv = true

	if verbose {
		fmt.Fprintf(os.Stderr, "fetching:hostname:%s:\n", hostname)
	}

	res := &http.Response{}

	if urls {

		url := hostname
		res, err = http.Head(url)
		defer res.Body.Close()

		// req, err = http.NewRequest("HEAD", url, nil)

		if err != nil {
			fmt.Println(err.Error())
			rv = false
			return
		}

	} else {

		client := &http.Client{Timeout: time.Duration(timeout) * time.Second}

		// had to allocate this or the SetBasicAuth will panic
		headers := make(map[string][]string)
		hostPort := fmt.Sprintf("%s:%d", hostname, port)

		if verbose {

			fmt.Fprintf(os.Stderr, "adding hostPort:%s:%d:path:%s:\n", hostname, port, path)

		}
		req := &http.Request{
			Method: "HEAD",
			// Host:  hostPort,
			URL: &url.URL{
				Host:   hostPort,
				Scheme: "http",
				Opaque: path,
			},
			Header: headers,
		}

		if auth != "" {

			up := strings.SplitN(auth, ":", 2)

			if verbose {

				fmt.Fprintf(os.Stderr, "Doing auth with:username:%s:password:%s:", up[0], up[1])

			}
			req.SetBasicAuth(up[0], up[1])

		}

		if verbose {

			dump, _ := httputil.DumpRequestOut(req, true)
			fmt.Fprintf(os.Stderr, "%s", dump)

		}

		res, err = client.Do(req)
		if err != nil {
			fmt.Println(err.Error())
			rv = false
			return
		}

		defer res.Body.Close()
		_, err = ioutil.ReadAll(res.Body)

	}

	// res, err := http.Head(url)
	// if err != nil {
	// 	fmt.Println(err.Error())
	// 	rv = false
	// 	return
	// }

	if verbose {

		fmt.Println(res.Status)
		for k, v := range res.Header {
			fmt.Println(k+":", v)
		}

	}

	if res.StatusCode != http.StatusOK {
		rv = false
	}

	return
}

func main() {

	status := "OK"
	rv := 0
	name := "Bulk HTTP"
	bad := 0
	total := 0

	// this needs improvement. the number of spaces here has to equal the number of chars in the badHosts append line suffix
	badHosts := []byte("  ")

	verbose := flag.Bool("v", false, "verbose output")
	warn := flag.Int("w", 10, "warning level - number of non-200s or percentage of non-200s (default is numeric not percentage)")
	crit := flag.Int("c", 20, "critical level - number of non-200s or percentage of non-200s (default is numeric not percentage)")
	timeout := flag.Int("t", 2, "timeout in seconds - don't wait.  Do Head requests and don't wait.")
	// pct := flag.Bool("pct", false, "interpret warming and critical levels are percentages")
	path := flag.String("path", "", "optional path to append to the stdin lines - these will not be urlencoded. This is ignored is the urls option is given (not implemented yet).")
	file := flag.String("file", "", "optional path to read data from a file instead of stdin.  If its a dash then read from stdin - these will not be urlencoded")
	port := flag.Int("port", 80, "optional port for the http request - ignored if urls is specified")
	urls := flag.Bool("urls", false, "Assume the input data is full urls - its normally a list of hostnames")
	auth := flag.String("auth", "", "Do basic auth with this username:passwd - ignored if urls is specified - make this use .netrc instead")

	flag.Usage = func() {

		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, `
	Read hostnames from a file or STDIN and do a single nagios check over
	them all.  Just check for 200s.  Warning and Critical are either
	percentages of the total, or a regular numeric thresholds.

	The output contains the hostname of any non-200 reporting hosts.

	Skip input lines that are commented out with shell style comments
	like /^#/.

	Do Head requests since we don't care about the content.  Make this
	optional some day.

	Also make this read from a file of urls with a -f option.

	Make the auth configurable from the cli

    	`)

		flag.PrintDefaults()
	}

	flag.Parse()

	if len(flag.Args()) > 0 {

		flag.Usage()
		os.Exit(3)

	}

	// it urls is specified, the input is full urls to be used enmasse and to be url encoded
	if *urls {
		*path = ""
	}

	// defer func() {
	// 	if err := recover(); err != nil {
	// 		fmt.Println(name+" Unknown: ", err)
	// 		os.Exit(3)
	// 	}
	// }()

	if file == nil || *file == "" {
		flag.Usage()
		os.Exit(3)
	}

	// inputSource := &io.Reader
	inputSource := os.Stdin

	if (*file)[0] != "-"[0] {

		var err error

		inputSource, err = os.Open(*file)

		if err != nil {

			fmt.Printf("Couldn't open the specified input file:%s:error:%v:\n\n", name, err)
			flag.Usage()
			os.Exit(3)

		}

	}

	scanner := bufio.NewScanner(inputSource)
	for scanner.Scan() {

		total++

		hostname := scanner.Text()

		if hostname[0] == "#"[0] {

			if *verbose {

				fmt.Printf("skipping:%s:\n", hostname)

			}

			continue
		}

		if *verbose {

			fmt.Printf("working on:%s:\n", hostname)

		}

		goodCheck, err := get(hostname, *port, *path, *auth, *urls, *verbose, *timeout)
		if err != nil {

			fmt.Printf("%s get error: %T %s %#v\n", name, err, err, err)

			continue

		}

		if !goodCheck {
			badHosts = append(badHosts, hostname...)
			badHosts = append(badHosts, ", "...)
			bad++
		}

	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}

	if bad >= *crit {
		status = "Critical"
		rv = 1
	} else if bad >= *warn {
		status = "Warning"
		rv = 2
	}

	fmt.Printf("%s %s: %d |%s\n", name, status, bad, badHosts[:len(badHosts)-2])
	os.Exit(rv)
}
<|endoftext|>


<commit_before>package view

import (
	"database/sql"
	"errors"
	"log"

	"github.com/sjmudd/ps-top/logger"
	"github.com/sjmudd/ps-top/table"
)

// Code represents the type of information to view (as an int)
type Code int

// View* constants represent different views we can see
const (
	ViewNone    Code = iota
	ViewLatency Code = iota // view the table latency information
	ViewOps     Code = iota // view the table information by number of operations
	ViewIO      Code = iota // view the file I/O information
	ViewLocks   Code = iota
	ViewUsers   Code = iota
	ViewMutex   Code = iota
	ViewStages  Code = iota
	ViewMemory  Code = iota // view memory usage (5.7 only)
)

// View holds the integer type of view (maybe need to fix this setup)
type View struct {
	code Code
}

var (
	names  map[Code]string       // map View* to a string name
	tables map[Code]table.Access // map a view to a table name and whether it's selectable or not

	nextView map[Code]Code // map from one view to the next taking into account invalid views
	prevView map[Code]Code // map from one view to the next taking into account invalid views
)

func init() {
	names = make(map[Code]string)

	names[ViewLatency] = "table_io_latency"
	names[ViewOps] = "table_io_ops"
	names[ViewIO] = "file_io_latency"
	names[ViewLocks] = "table_lock_latency"
	names[ViewUsers] = "user_latency"
	names[ViewMutex] = "mutex_latency"
	names[ViewStages] = "stages_latency"
	names[ViewMemory] = "memory_usage"

	tables = make(map[Code]table.Access)

	tables[ViewLatency] = table.NewAccess("performance_schema", "table_io_waits_summary_by_table")
	tables[ViewOps] = table.NewAccess("performance_schema", "table_io_waits_summary_by_table")
	tables[ViewIO] = table.NewAccess("performance_schema", "file_summary_by_instance")
	tables[ViewLocks] = table.NewAccess("performance_schema", "table_lock_waits_summary_by_table")
	tables[ViewUsers] = table.NewAccess("information_schema", "processlist")
	tables[ViewMutex] = table.NewAccess("performance_schema", "events_waits_summary_global_by_event_name")
	tables[ViewStages] = table.NewAccess("performance_schema", "events_stages_summary_global_by_event_name")
	tables[ViewMemory] = table.NewAccess("performance_schema", "memory_summary_global_by_event_name")

}

// ValidateViews check which views are readable. If none are we give a fatal error
func ValidateViews(dbh *sql.DB) error {
	var count int
	var status string
	logger.Println("Validating access to views...")

	for v := range names {
		ta := tables[v]
		e := ta.CheckSelectError(dbh)
		suffix := ""
		if e == nil {
			status = "is"
			count++
		} else {
			status = "IS NOT"
			suffix = " " + e.Error()
		}
		tables[v] = ta
		logger.Println(v.String() + ": " + ta.Name() + " " + status + " SELECTable" + suffix)
	}

	if count == 0 {
		return errors.New("None of the required tables are SELECTable. Giving up")
	}
	logger.Println(count, "of", len(names), "view(s) are SELECTable, continuing")

	setPrevAndNextViews()

	return nil
}

/* set the previous and next views taking into account any invalid views

name     selectable?    prev      next
----     -----------    ----      ----
v1       false          v4        v2
v2       true           v4        v4
v3       false          v2        v4
v4       true           v2        v2
v5       false          v4        v2

*/

func setPrevAndNextViews() {
	logger.Println("view.setPrevAndNextViews()...")
	nextView = make(map[Code]Code)
	prevView = make(map[Code]Code)

	// reset values
	for v := range names {
		nextView[v] = ViewNone
		prevView[v] = ViewNone
	}

	// Cleaner way to do this? Probably. Fix later.
	prevCodeOrder := []Code{ViewMemory, ViewStages, ViewMutex, ViewUsers, ViewLocks, ViewIO, ViewOps, ViewLatency}
	nextCodeOrder := []Code{ViewLatency, ViewOps, ViewIO, ViewLocks, ViewUsers, ViewMutex, ViewStages, ViewMemory}
	prevView = setValidByValues(prevCodeOrder)
	nextView = setValidByValues(nextCodeOrder)

	// print out the results
	logger.Println("Final mapping of view order:")
	for i := range nextCodeOrder {
		logger.Println("view:", nextCodeOrder[i], ", prev:", prevView[nextCodeOrder[i]], ", next:", nextView[nextCodeOrder[i]])
	}
}

// setValidNextByValues returns a map of Code -> Code where the mapping points to the "next"
// Code. The order is determined by the input Code slice. Only Selectable Views are considered
// for the mapping with the other views pointing to the first Code provided.
func setValidByValues(orderedCodes []Code) map[Code]Code {
	logger.Println("view.setValidByValues()")
	orderedMap := make(map[Code]Code)

	// reset orderedCodes
	for i := range orderedCodes {
		orderedMap[orderedCodes[i]] = ViewNone
	}

	first, last := ViewNone, ViewNone

	// first pass, try to find values and point forward to next position if known.
	// we must find at least one value view in the first pass.
	for i := range []int{1, 2} {
		for i := range orderedCodes {
			currentPos := orderedCodes[i]
			if tables[currentPos].SelectError() == nil {
				if first == ViewNone {
					first = currentPos
				}
				if last != ViewNone {
					orderedMap[last] = currentPos
				}
				last = currentPos
			}
		}
		if i == 1 {
			// not found a valid view so something is up. Give up!
			if first == ViewNone {
				log.Panic("setValidByValues() can't find a Selectable view! (shouldn't be here)")
			}
		}
	}

	// final pass viewNone entries should point to first
	for i := range orderedCodes {
		currentPos := orderedCodes[i]
		if tables[currentPos].SelectError() != nil {
			orderedMap[currentPos] = first
		}
	}

	return orderedMap
}

// SetNext changes the current view to the next one
func (v *View) SetNext() Code {
	v.code = nextView[v.code]

	return v.code
}

// SetPrev changes the current view to the previous one
func (v *View) SetPrev() Code {
	v.code = prevView[v.code]

	return v.code
}

// Set sets the view to the given view (by Code)
func (v *View) Set(viewCode Code) {
	v.code = viewCode

	if tables[v.code].SelectError() != nil {
		v.code = nextView[v.code]
	}
}

// SetByName sets the view based on its name.
// - If we provide an empty name then use the default.
// - If we don't provide a valid name then give an error
func (v *View) SetByName(name string) {
	logger.Println("View.SetByName(" + name + ")")
	if name == "" {
		logger.Println("View.SetByName(): name is empty so setting to:", ViewLatency.String())
		v.Set(ViewLatency)
		return
	}

	for i := range names {
		if name == names[i] {
			v.code = Code(i)
			logger.Println("View.SetByName(", name, ")")
			return
		}
	}

	// suggest what should be used
	allViews := ""
	for i := range names {
		allViews = allViews + " " + names[i]
	}

	// no need for now to strip off leading space from allViews.
	log.Fatal("Asked for a view name, '", name, "' which doesn't exist. Try one of:", allViews)
}

// Get returns the Code version of the current view
func (v View) Get() Code {
	return v.code
}

// Name returns the string version of the current view
func (v View) Name() string {
	return v.code.String()
}

func (s Code) String() string {
	return names[s]
}
<commit_msg>github.com/sjmudd/ps-top/view: add more comments<commit_after>package view

import (
	"database/sql"
	"errors"
	"log"

	"github.com/sjmudd/ps-top/logger"
	"github.com/sjmudd/ps-top/table"
)

// Code represents the type of information to view (as an int)
type Code int

// View* constants represent different views we can see
const (
	ViewNone    Code = iota // view nothing (should never be set)
	ViewLatency Code = iota // view the table latency information
	ViewOps     Code = iota // view the table information by number of operations
	ViewIO      Code = iota // view the file I/O information
	ViewLocks   Code = iota // view lock information
	ViewUsers   Code = iota // view user information
	ViewMutex   Code = iota // view mutex information
	ViewStages  Code = iota // view SQL stages information
	ViewMemory  Code = iota // view memory usage (5.7 only)
)

// View holds the integer type of view (maybe need to fix this setup)
type View struct {
	code Code
}

var (
	names  map[Code]string       // map View* to a string name
	tables map[Code]table.Access // map a view to a table name and whether it's selectable or not

	nextView map[Code]Code // map from one view to the next taking into account invalid views
	prevView map[Code]Code // map from one view to the next taking into account invalid views
)

func init() {
	names = make(map[Code]string)

	names[ViewLatency] = "table_io_latency"
	names[ViewOps] = "table_io_ops"
	names[ViewIO] = "file_io_latency"
	names[ViewLocks] = "table_lock_latency"
	names[ViewUsers] = "user_latency"
	names[ViewMutex] = "mutex_latency"
	names[ViewStages] = "stages_latency"
	names[ViewMemory] = "memory_usage"

	tables = make(map[Code]table.Access)

	tables[ViewLatency] = table.NewAccess("performance_schema", "table_io_waits_summary_by_table")
	tables[ViewOps] = table.NewAccess("performance_schema", "table_io_waits_summary_by_table")
	tables[ViewIO] = table.NewAccess("performance_schema", "file_summary_by_instance")
	tables[ViewLocks] = table.NewAccess("performance_schema", "table_lock_waits_summary_by_table")
	tables[ViewUsers] = table.NewAccess("information_schema", "processlist")
	tables[ViewMutex] = table.NewAccess("performance_schema", "events_waits_summary_global_by_event_name")
	tables[ViewStages] = table.NewAccess("performance_schema", "events_stages_summary_global_by_event_name")
	tables[ViewMemory] = table.NewAccess("performance_schema", "memory_summary_global_by_event_name")
}

// ValidateViews check which views are readable. If none are we give a fatal error
func ValidateViews(dbh *sql.DB) error {
	var count int
	var status string
	logger.Println("Validating access to views...")

	// determine which of the defined views is valid because the underlying table access works
	for v := range names {
		ta := tables[v]
		e := ta.CheckSelectError(dbh)
		suffix := ""
		if e == nil {
			status = "is"
			count++
		} else {
			status = "IS NOT"
			suffix = " " + e.Error()
		}
		tables[v] = ta
		logger.Println(v.String() + ": " + ta.Name() + " " + status + " SELECTable" + suffix)
	}

	if count == 0 {
		return errors.New("None of the required tables are SELECTable. Giving up")
	}
	logger.Println(count, "of", len(names), "view(s) are SELECTable, continuing")

	setPrevAndNextViews()

	return nil
}

/* set the previous and next views taking into account any invalid views

name     selectable?    prev      next
----     -----------    ----      ----
v1       false          v4        v2
v2       true           v4        v4
v3       false          v2        v4
v4       true           v2        v2
v5       false          v4        v2

*/

func setPrevAndNextViews() {
	logger.Println("view.setPrevAndNextViews()...")
	nextView = make(map[Code]Code)
	prevView = make(map[Code]Code)

	// reset values
	for v := range names {
		nextView[v] = ViewNone
		prevView[v] = ViewNone
	}

	// Cleaner way to do this? Probably. Fix later.
	prevCodeOrder := []Code{ViewMemory, ViewStages, ViewMutex, ViewUsers, ViewLocks, ViewIO, ViewOps, ViewLatency}
	nextCodeOrder := []Code{ViewLatency, ViewOps, ViewIO, ViewLocks, ViewUsers, ViewMutex, ViewStages, ViewMemory}
	prevView = setValidByValues(prevCodeOrder)
	nextView = setValidByValues(nextCodeOrder)

	// print out the results
	logger.Println("Final mapping of view order:")
	for i := range nextCodeOrder {
		logger.Println("view:", nextCodeOrder[i], ", prev:", prevView[nextCodeOrder[i]], ", next:", nextView[nextCodeOrder[i]])
	}
}

// setValidNextByValues returns a map of Code -> Code where the mapping points to the "next"
// Code. The order is determined by the input Code slice. Only Selectable Views are considered
// for the mapping with the other views pointing to the first Code provided.
func setValidByValues(orderedCodes []Code) map[Code]Code {
	logger.Println("view.setValidByValues()")
	orderedMap := make(map[Code]Code)

	// reset orderedCodes
	for i := range orderedCodes {
		orderedMap[orderedCodes[i]] = ViewNone
	}

	first, last := ViewNone, ViewNone

	// first pass, try to find values and point forward to next position if known.
	// we must find at least one value view in the first pass.
	for i := range []int{1, 2} {
		for i := range orderedCodes {
			currentPos := orderedCodes[i]
			if tables[currentPos].SelectError() == nil {
				if first == ViewNone {
					first = currentPos
				}
				if last != ViewNone {
					orderedMap[last] = currentPos
				}
				last = currentPos
			}
		}
		if i == 1 {
			// not found a valid view so something is up. Give up!
			if first == ViewNone {
				log.Panic("setValidByValues() can't find a Selectable view! (shouldn't be here)")
			}
		}
	}

	// final pass viewNone entries should point to first
	for i := range orderedCodes {
		currentPos := orderedCodes[i]
		if tables[currentPos].SelectError() != nil {
			orderedMap[currentPos] = first
		}
	}

	return orderedMap
}

// SetNext changes the current view to the next one
func (v *View) SetNext() Code {
	v.code = nextView[v.code]

	return v.code
}

// SetPrev changes the current view to the previous one
func (v *View) SetPrev() Code {
	v.code = prevView[v.code]

	return v.code
}

// Set sets the view to the given view (by Code)
func (v *View) Set(viewCode Code) {
	v.code = viewCode

	if tables[v.code].SelectError() != nil {
		v.code = nextView[v.code]
	}
}

// SetByName sets the view based on its name.
// - If we provide an empty name then use the default.
// - If we don't provide a valid name then give an error
func (v *View) SetByName(name string) {
	logger.Println("View.SetByName(" + name + ")")
	if name == "" {
		logger.Println("View.SetByName(): name is empty so setting to:", ViewLatency.String())
		v.Set(ViewLatency)
		return
	}

	for i := range names {
		if name == names[i] {
			v.code = Code(i)
			logger.Println("View.SetByName(", name, ")")
			return
		}
	}

	// suggest what should be used
	allViews := ""
	for i := range names {
		allViews = allViews + " " + names[i]
	}

	// no need for now to strip off leading space from allViews.
	log.Fatal("Asked for a view name, '", name, "' which doesn't exist. Try one of:", allViews)
}

// Get returns the Code version of the current view
func (v View) Get() Code {
	return v.code
}

// Name returns the string version of the current view
func (v View) Name() string {
	return v.code.String()
}

func (s Code) String() string {
	return names[s]
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/ToQoz/dou"
	_ "github.com/ToQoz/dou/jsonapi"
	"github.com/ToQoz/rome"
	"github.com/lestrrat/go-apache-logformat"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"
)

var (
	APIStatusOk              = 1
	APIStatusValidationError = 100
	APIStatusUnexpectedError = 900
	logger                   = apachelog.CombinedLog
)

// --- API Error type ---

type apiError struct {
	Message string `json:"message"`
}

func newAPIError(err error) *apiError {
	return &apiError{Message: err.Error()}
}

type apiErrors struct {
	Errors []*apiError `json:"errors"`
}

func newAPIErrors(errs []error) *apiErrors {
	aErrs := &apiErrors{}

	for _, err := range errs {
		aErrs.Errors = append(aErrs.Errors, newAPIError(err))
	}

	return aErrs
}

// --- Example struct ---

var users = []*User{}

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func (u *User) Validate() []error {
	var errs []error

	if u.Name == "" {
		errs = append(errs, errors.New("User: name is required"))
	}

	if u.Email == "" {
		errs = append(errs, errors.New("User: email is required"))
	}

	return errs
}

func (u *User) Save() error {
	users = append(users, u)
	return nil
}

func main() {
	defer teardown()

	// --- Setup Router ---
	// ! You can use router keeping interface `api.Router` instead of github.com/ToQoz/rome
	router := rome.NewRouter()
	router.NotFoundFunc(func(w http.ResponseWriter, r *http.Request) {
		j, err := json.Marshal(map[string]string{
			"message":           http.StatusText(http.StatusNotFound),
			"documentation_url": "http://toqoz.net",
		})

		if err != nil {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}

		w.WriteHeader(http.StatusNotFound)
		// skip wrote bytesyze
		_, err = fmt.Fprintln(w, string(j))

		if err != nil {
			log.Printf("dou: fail to fmt.Fpintln(http.ResponseWriter, string)\n%v", err)
		}
	})

	// --- Setup API ---
	api, err := dou.NewAPI("jsonapi")
	api.Handler = router
	//api, err := dou.NewAPI("jsonapi")
	if err != nil {
		log.Fatal(err)
	}

	api.BeforeDispatch = func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) {
		// Call default
		w, r = api.Plugin.BeforeDispatch(w, r)

		lw := apachelog.NewLoggingWriter(w, r, logger)
		return lw, r
	}

	api.AfterDispatch = func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) {
		// Call default
		w, r = api.Plugin.AfterDispatch(w, r)

		if lw, ok := w.(*apachelog.LoggingWriter); ok {
			lw.EmitLog()
		}

		return w, r
	}

	api.ReadTimeout = 10 * time.Second
	api.WriteTimeout = 10 * time.Second
	api.MaxHeaderBytes = 1 << 20

	// --- Map routes ---
	router.GetFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		api.APIStatus(w, APIStatusOk)
		api.Ok(w, users, http.StatusOK)
	})

	router.GetFunc("/error", func(w http.ResponseWriter, r *http.Request) {
		api.APIStatus(w, APIStatusUnexpectedError)
		api.Error(w, map[string]string{"message": "Internal server error"}, http.StatusInternalServerError)
	})

	// Try Ok    $ curl -X POST -d 'name=ToQoz&email=toqoz403@gmail.com' -D - :8099/users
	// Try Error $ curl -X POST -D - :8099/users
	router.PostFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		u := &User{
			Name:  r.FormValue("name"),
			Email: r.FormValue("email"),
		}

		errs := u.Validate()

		if len(errs) > 0 {
			api.APIStatus(w, APIStatusValidationError)
			api.Error(w, newAPIErrors(errs), 422)
			return
		}

		err := u.Save()

		if err != nil {
			api.APIStatus(w, APIStatusUnexpectedError)
			api.Error(w, newAPIErrors(errs), http.StatusInternalServerError)
			return
		}

		api.APIStatus(w, APIStatusOk)
		api.Ok(w, u, http.StatusCreated)
	})

	// --- Create listener ---
	// You can use utility, for example github.com/lestrrat/go-server-starter-listener etc.
	l, err := net.Listen("tcp", ":8099")

	if err != nil {
		log.Printf("Could not listen: %s", ":8099")
		teardown()
		os.Exit(1)
	}

	log.Printf("Listen: %s", ":8099")

	// --- Handle C-c ---
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		for sig := range c {
			log.Print("Stopping the server...")

			switch sig {
			case os.Interrupt:
				// --- Stop Server ---
				api.Stop()
				return
			default:
				log.Print("Receive unknown signal...")
			}
		}
	}()

	// --- Run Server ---
	api.Run(l)
}

func teardown() {
	log.Print("Tearing down...")
	log.Print("Finished - bye bye.  ;-)")
}
<commit_msg>Don't capitalize error strings. Follow golint.<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/ToQoz/dou"
	_ "github.com/ToQoz/dou/jsonapi"
	"github.com/ToQoz/rome"
	"github.com/lestrrat/go-apache-logformat"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"
)

var (
	APIStatusOk              = 1
	APIStatusValidationError = 100
	APIStatusUnexpectedError = 900
	logger                   = apachelog.CombinedLog
)

// --- API Error type ---

type apiError struct {
	Message string `json:"message"`
}

func newAPIError(err error) *apiError {
	return &apiError{Message: err.Error()}
}

type apiErrors struct {
	Errors []*apiError `json:"errors"`
}

func newAPIErrors(errs []error) *apiErrors {
	aErrs := &apiErrors{}

	for _, err := range errs {
		aErrs.Errors = append(aErrs.Errors, newAPIError(err))
	}

	return aErrs
}

// --- Example struct ---

var users = []*User{}

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func (u *User) Validate() []error {
	var errs []error

	if u.Name == "" {
		errs = append(errs, errors.New("user: name is required"))
	}

	if u.Email == "" {
		errs = append(errs, errors.New("user: email is required"))
	}

	return errs
}

func (u *User) Save() error {
	users = append(users, u)
	return nil
}

func main() {
	defer teardown()

	// --- Setup Router ---
	// ! You can use router keeping interface `api.Router` instead of github.com/ToQoz/rome
	router := rome.NewRouter()
	router.NotFoundFunc(func(w http.ResponseWriter, r *http.Request) {
		j, err := json.Marshal(map[string]string{
			"message":           http.StatusText(http.StatusNotFound),
			"documentation_url": "http://toqoz.net",
		})

		if err != nil {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}

		w.WriteHeader(http.StatusNotFound)
		// skip wrote bytesyze
		_, err = fmt.Fprintln(w, string(j))

		if err != nil {
			log.Printf("dou: fail to fmt.Fpintln(http.ResponseWriter, string)\n%v", err)
		}
	})

	// --- Setup API ---
	api, err := dou.NewAPI("jsonapi")
	api.Handler = router
	//api, err := dou.NewAPI("jsonapi")
	if err != nil {
		log.Fatal(err)
	}

	api.BeforeDispatch = func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) {
		// Call default
		w, r = api.Plugin.BeforeDispatch(w, r)

		lw := apachelog.NewLoggingWriter(w, r, logger)
		return lw, r
	}

	api.AfterDispatch = func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) {
		// Call default
		w, r = api.Plugin.AfterDispatch(w, r)

		if lw, ok := w.(*apachelog.LoggingWriter); ok {
			lw.EmitLog()
		}

		return w, r
	}

	api.ReadTimeout = 10 * time.Second
	api.WriteTimeout = 10 * time.Second
	api.MaxHeaderBytes = 1 << 20

	// --- Map routes ---
	router.GetFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		api.APIStatus(w, APIStatusOk)
		api.Ok(w, users, http.StatusOK)
	})

	router.GetFunc("/error", func(w http.ResponseWriter, r *http.Request) {
		api.APIStatus(w, APIStatusUnexpectedError)
		api.Error(w, map[string]string{"message": "Internal server error"}, http.StatusInternalServerError)
	})

	// Try Ok    $ curl -X POST -d 'name=ToQoz&email=toqoz403@gmail.com' -D - :8099/users
	// Try Error $ curl -X POST -D - :8099/users
	router.PostFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		u := &User{
			Name:  r.FormValue("name"),
			Email: r.FormValue("email"),
		}

		errs := u.Validate()

		if len(errs) > 0 {
			api.APIStatus(w, APIStatusValidationError)
			api.Error(w, newAPIErrors(errs), 422)
			return
		}

		err := u.Save()

		if err != nil {
			api.APIStatus(w, APIStatusUnexpectedError)
			api.Error(w, newAPIErrors(errs), http.StatusInternalServerError)
			return
		}

		api.APIStatus(w, APIStatusOk)
		api.Ok(w, u, http.StatusCreated)
	})

	// --- Create listener ---
	// You can use utility, for example github.com/lestrrat/go-server-starter-listener etc.
	l, err := net.Listen("tcp", ":8099")

	if err != nil {
		log.Printf("Could not listen: %s", ":8099")
		teardown()
		os.Exit(1)
	}

	log.Printf("Listen: %s", ":8099")

	// --- Handle C-c ---
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		for sig := range c {
			log.Print("Stopping the server...")

			switch sig {
			case os.Interrupt:
				// --- Stop Server ---
				api.Stop()
				return
			default:
				log.Print("Receive unknown signal...")
			}
		}
	}()

	// --- Run Server ---
	api.Run(l)
}

func teardown() {
	log.Print("Tearing down...")
	log.Print("Finished - bye bye.  ;-)")
}
<|endoftext|>


<commit_before>package couchdb

import (
	"testing"
)

type ViewDocument struct {
	Document
	Views map[string]interface{} `json:"views"`
}

func (doc *ViewDocument) GetDocument() *Document {
	return &doc.Document
}

type DataDocument struct {
	Document
	Type string `json:"type"`
	Foo  string `json:"foo"`
	Beep string `json:"beep"`
}

func (doc *DataDocument) GetDocument() *Document {
	return &doc.Document
}

var c_view = Client{"http://127.0.0.1:5984/"}
var db_view = c_view.Use("gotest")

func TestViewBefore(t *testing.T) {

	// create database
	t.Log("creating database...")
	_, err := c_view.Create("gotest")
	if err != nil {
		t.Fatal(err)
	}

	// create design document
	t.Log("creating design document...")
	view := make(map[string]string)
	view["map"] =
		`
    function(doc){
      if (doc.type == 'data') emit(doc.foo)
    }
  `
	views := make(map[string]interface{})
	views["foo"] = view

	design := &ViewDocument{
		Document: Document{
			Id: "_design/test",
		},
		Views: views,
	}

	_, err = db_view.Post(design)
	if err != nil {
		t.Fatal(err)
	}

	// create dummy data
	t.Log("creating dummy data...")
	doc1 := &DataDocument{
		Type: "data",
		Foo:  "foo1",
		Beep: "beep1",
	}

	_, err = db_view.Post(doc1)
	if err != nil {
		t.Fatal(err)
	}

	doc2 := &DataDocument{
		Type: "data",
		Foo:  "foo2",
		Beep: "beep2",
	}

	_, err = db_view.Post(doc2)
	if err != nil {
		t.Fatal(err)
	}
}

func TestViewGet(t *testing.T) {
	view := db_view.View("test")
	params := QueryParameters{}
	res, err := view.Get("foo", params)
	if err != nil {
		t.Fatal(err)
	}
	if res.TotalRows != 2 || res.Offset != 0 {
		t.Error("view get error")
	}
}

func TestViewGetWithQueryParameters(t *testing.T) {
  view := db_view.View("test")
  params := QueryParameters{
    Key: "\"foo1\"",
  }
  res, err := view.Get("foo", params)
  if err != nil {
    t.Fatal(err)
  }
	if len(res.Rows) != 1 {
		t.Error("view get error")
	}
}

func TestViewAfter(t *testing.T) {
	t.Log("deleting test data for view tests...")
	_, err := c_view.Delete("gotest")
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>go fmt<commit_after>package couchdb

import (
	"testing"
)

type ViewDocument struct {
	Document
	Views map[string]interface{} `json:"views"`
}

func (doc *ViewDocument) GetDocument() *Document {
	return &doc.Document
}

type DataDocument struct {
	Document
	Type string `json:"type"`
	Foo  string `json:"foo"`
	Beep string `json:"beep"`
}

func (doc *DataDocument) GetDocument() *Document {
	return &doc.Document
}

var c_view = Client{"http://127.0.0.1:5984/"}
var db_view = c_view.Use("gotest")

func TestViewBefore(t *testing.T) {

	// create database
	t.Log("creating database...")
	_, err := c_view.Create("gotest")
	if err != nil {
		t.Fatal(err)
	}

	// create design document
	t.Log("creating design document...")
	view := make(map[string]string)
	view["map"] =
		`
    function(doc){
      if (doc.type == 'data') emit(doc.foo)
    }
  `
	views := make(map[string]interface{})
	views["foo"] = view

	design := &ViewDocument{
		Document: Document{
			Id: "_design/test",
		},
		Views: views,
	}

	_, err = db_view.Post(design)
	if err != nil {
		t.Fatal(err)
	}

	// create dummy data
	t.Log("creating dummy data...")
	doc1 := &DataDocument{
		Type: "data",
		Foo:  "foo1",
		Beep: "beep1",
	}

	_, err = db_view.Post(doc1)
	if err != nil {
		t.Fatal(err)
	}

	doc2 := &DataDocument{
		Type: "data",
		Foo:  "foo2",
		Beep: "beep2",
	}

	_, err = db_view.Post(doc2)
	if err != nil {
		t.Fatal(err)
	}
}

func TestViewGet(t *testing.T) {
	view := db_view.View("test")
	params := QueryParameters{}
	res, err := view.Get("foo", params)
	if err != nil {
		t.Fatal(err)
	}
	if res.TotalRows != 2 || res.Offset != 0 {
		t.Error("view get error")
	}
}

func TestViewGetWithQueryParameters(t *testing.T) {
	view := db_view.View("test")
	params := QueryParameters{
		Key: "\"foo1\"",
	}
	res, err := view.Get("foo", params)
	if err != nil {
		t.Fatal(err)
	}
	if len(res.Rows) != 1 {
		t.Error("view get error")
	}
}

func TestViewAfter(t *testing.T) {
	t.Log("deleting test data for view tests...")
	_, err := c_view.Delete("gotest")
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright Banrai LLC. All rights reserved. Use of this source code is
// governed by the license that can be found in the LICENSE file.

package emailer

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/smtp"
	"os"
	"text/template"
)

const (
	MAIL_SERVER = "localhost"
	MAIL_PORT   = 25

	LINE_MAX_LEN = 500 // for splitting encoded attachment data

	// templates for generating the message components
	ADDRESS    = "\"{{.DisplayName}}\" <{{.Address}}>"
	HEADERS    = "From: {{.Sender}}\r\nTo: {{.Recipient}}\r\nSubject: {{.Subject}}\r\nMIME-Version: 1.0\r\nContent-Type: multipart/alternative; boundary=\"{{.Boundary}}\"\r\n"
	BODY       = "\r\n--{{.Boundary}}\r\nContent-Type: {{.ContentType}}\r\n\r\n{{.MessageBody}}"
	ATTACHMENT = "\r\n--{{.Boundary}}\r\nContent-Type: {{.ContentType}}; name=\"{{.FileLocation}}\"\r\nContent-Transfer-Encoding:base64\r\nContent-Disposition: attachment; filename=\"{{.FileName}}\"\r\n\r\n{{.EncodedFileData}}"

	// message body mime types
	TEXT_MIME = "text/plain"
	HTML_MIME = "text/html"
)

type EmailAddress struct {
	DisplayName string
	Address     string
}

type EmailHeaders struct {
	Sender    string
	Recipient string
	Subject   string
	Boundary  string
}

type EmailBody struct {
	ContentType string
	MessageBody string
	Boundary    string
}

type EmailAttachment struct {
	ContentType     string
	EncodedFileData string
	Boundary        string
	// read content from a file
	FileLocation string
	FileName     string
	// or provide it here
	Contents string
}

// GenerateBoundary produces a random string that can be used for the email
// multipart boundary marker
func GenerateBoundary() string {
	f, e := os.OpenFile("/dev/urandom", os.O_RDONLY, 0)
	defer f.Close()

	if e != nil {
		return ""
	} else {
		b := make([]byte, 16)
		f.Read(b)
		return fmt.Sprintf("%x", b)
	}
}

func GenerateAddress(context *EmailAddress) (string, error) {
	var doc bytes.Buffer
	t := template.Must(template.New("ADDRESS").Parse(ADDRESS))
	err := t.Execute(&doc, context)
	return doc.String(), err
}

func GenerateHeaders(sender, recipient, subject, boundary string) (string, error) {
	var doc bytes.Buffer
	context := &EmailHeaders{sender, recipient, subject, boundary}
	t := template.Must(template.New("HEADERS").Parse(HEADERS))
	err := t.Execute(&doc, context)
	return doc.String(), err
}

func GenerateBody(message, contentType, boundary string) (string, error) {
	var doc bytes.Buffer
	context := &EmailBody{contentType, message, boundary}
	t := template.Must(template.New("BODY").Parse(BODY))
	err := t.Execute(&doc, context)
	return doc.String(), err
}

func GenerateAttachment(attachment *EmailAttachment) (string, error) {
	var doc, buf bytes.Buffer
	var encoded string

	if attachment.Contents != "" {
		// content to encode is already included
		encoded = base64.StdEncoding.EncodeToString([]byte(attachment.Contents))
	} else {
		// read the content from the file attachment
		content, contentErr := ioutil.ReadFile(attachment.FileLocation)
		if contentErr != nil {
			return "", contentErr
		}
		encoded = base64.StdEncoding.EncodeToString(content)
	}

	// split the encoded data into individual lines
	// and append them to the byte buffer
	lines := len(encoded) / LINE_MAX_LEN
	for i := 0; i < lines; i++ {
		buf.WriteString(encoded[i*LINE_MAX_LEN:(i+1)*LINE_MAX_LEN] + "\n")
	}
	// don't forget the last line in the buffer
	buf.WriteString(encoded[lines*LINE_MAX_LEN:])
	attachment.EncodedFileData = buf.String()

	// can now process the template
	t := template.Must(template.New("ATTACHMENT").Parse(ATTACHMENT))
	err := t.Execute(&doc, attachment)
	return doc.String(), err
}

// SendFromServer transmits the given message, with optional attachments,
// via the defined mail server and port
func SendFromServer(subject, messageText, messageHtml, server string, sender, recipient *EmailAddress, attachments []*EmailAttachment, port int) error {
	var buf bytes.Buffer
	boundary := GenerateBoundary()

	from, fromErr := GenerateAddress(sender)
	if fromErr != nil {
		return fromErr
	}

	to, toErr := GenerateAddress(recipient)
	if toErr != nil {
		return toErr
	}

	hdr, hdrErr := GenerateHeaders(from, to, subject, boundary)
	if hdrErr != nil {
		return hdrErr
	}
	buf.WriteString(hdr)

	body, bodyErr := GenerateBody(messageText, TEXT_MIME, boundary)
	if bodyErr != nil {
		return bodyErr
	}
	buf.WriteString(body)

	// message body in html format is optional
	if len(messageHtml) > 0 {
		htmlBody, htmlBodyErr := GenerateBody(messageHtml, HTML_MIME, boundary)
		if htmlBodyErr != nil {
			return htmlBodyErr
		}
		buf.WriteString(htmlBody)
	}

	for _, a := range attachments {
		a.Boundary = boundary
		attach, attachErr := GenerateAttachment(a)
		if attachErr != nil {
			return attachErr
		}
		buf.WriteString(attach)
	}

	// add the closing boundary marker
	buf.WriteString("\r\n--")
	buf.WriteString(boundary)
	buf.WriteString("--")

	// connect to the mail server + port
	c, cErr := smtp.Dial(fmt.Sprintf("%s:%d", server, port))
	if cErr != nil {
		return cErr
	}

	// set the sender and recipient (raw email address strings)
	c.Mail(sender.Address)
	c.Rcpt(recipient.Address)

	// stream the full email data
	wc, err := c.Data()
	if err != nil {
		return err
	}
	defer wc.Close()

	_, err = buf.WriteTo(wc)
	return err
}

// Send transmits the given message, with optional attachments, via the
// default mail server (localhost) and port (25)
func Send(subject, messageText, messageHtml string, sender, recipient *EmailAddress, attachments []*EmailAttachment) error {
	return SendFromServer(subject, messageText, messageHtml, MAIL_SERVER, sender, recipient, attachments, MAIL_PORT)
}
<commit_msg>Seeing if I can stick with a relatively simple internal email header structure, w/o having to do this: stackoverflow.com/a/40420648/633961<commit_after>// Copyright Banrai LLC. All rights reserved. Use of this source code is
// governed by the license that can be found in the LICENSE file.

package emailer

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/smtp"
	"os"
	"text/template"
)

const (
	MAIL_SERVER = "localhost"
	MAIL_PORT   = 25

	LINE_MAX_LEN = 500 // for splitting encoded attachment data

	// templates for generating the message components
	ADDRESS    = "\"{{.DisplayName}}\" <{{.Address}}>"
	HEADERS    = "From: {{.Sender}}\r\nTo: {{.Recipient}}\r\nSubject: {{.Subject}}\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"{{.Boundary}}\"\r\n"
	BODY       = "\r\n--{{.Boundary}}\r\nContent-Type: {{.ContentType}}\r\n\r\n{{.MessageBody}}"
	ATTACHMENT = "\r\n--{{.Boundary}}\r\nContent-Type: {{.ContentType}}; name=\"{{.FileLocation}}\"\r\nContent-Transfer-Encoding:base64\r\nContent-Disposition: attachment; filename=\"{{.FileName}}\"\r\n\r\n{{.EncodedFileData}}"

	// message body mime types
	TEXT_MIME = "text/plain"
	HTML_MIME = "text/html"
)

type EmailAddress struct {
	DisplayName string
	Address     string
}

type EmailHeaders struct {
	Sender    string
	Recipient string
	Subject   string
	Boundary  string
}

type EmailBody struct {
	ContentType string
	MessageBody string
	Boundary    string
}

type EmailAttachment struct {
	ContentType     string
	EncodedFileData string
	Boundary        string
	// read content from a file
	FileLocation string
	FileName     string
	// or provide it here
	Contents string
}

// GenerateBoundary produces a random string that can be used for the email
// multipart boundary marker
func GenerateBoundary() string {
	f, e := os.OpenFile("/dev/urandom", os.O_RDONLY, 0)
	defer f.Close()

	if e != nil {
		return ""
	} else {
		b := make([]byte, 16)
		f.Read(b)
		return fmt.Sprintf("%x", b)
	}
}

func GenerateAddress(context *EmailAddress) (string, error) {
	var doc bytes.Buffer
	t := template.Must(template.New("ADDRESS").Parse(ADDRESS))
	err := t.Execute(&doc, context)
	return doc.String(), err
}

func GenerateHeaders(sender, recipient, subject, boundary string) (string, error) {
	var doc bytes.Buffer
	context := &EmailHeaders{sender, recipient, subject, boundary}
	t := template.Must(template.New("HEADERS").Parse(HEADERS))
	err := t.Execute(&doc, context)
	return doc.String(), err
}

func GenerateBody(message, contentType, boundary string) (string, error) {
	var doc bytes.Buffer
	context := &EmailBody{contentType, message, boundary}
	t := template.Must(template.New("BODY").Parse(BODY))
	err := t.Execute(&doc, context)
	return doc.String(), err
}

func GenerateAttachment(attachment *EmailAttachment) (string, error) {
	var doc, buf bytes.Buffer
	var encoded string

	if attachment.Contents != "" {
		// content to encode is already included
		encoded = base64.StdEncoding.EncodeToString([]byte(attachment.Contents))
	} else {
		// read the content from the file attachment
		content, contentErr := ioutil.ReadFile(attachment.FileLocation)
		if contentErr != nil {
			return "", contentErr
		}
		encoded = base64.StdEncoding.EncodeToString(content)
	}

	// split the encoded data into individual lines
	// and append them to the byte buffer
	lines := len(encoded) / LINE_MAX_LEN
	for i := 0; i < lines; i++ {
		buf.WriteString(encoded[i*LINE_MAX_LEN:(i+1)*LINE_MAX_LEN] + "\n")
	}
	// don't forget the last line in the buffer
	buf.WriteString(encoded[lines*LINE_MAX_LEN:])
	attachment.EncodedFileData = buf.String()

	// can now process the template
	t := template.Must(template.New("ATTACHMENT").Parse(ATTACHMENT))
	err := t.Execute(&doc, attachment)
	return doc.String(), err
}

// SendFromServer transmits the given message, with optional attachments,
// via the defined mail server and port
func SendFromServer(subject, messageText, messageHtml, server string, sender, recipient *EmailAddress, attachments []*EmailAttachment, port int) error {
	var buf bytes.Buffer
	boundary := GenerateBoundary()

	from, fromErr := GenerateAddress(sender)
	if fromErr != nil {
		return fromErr
	}

	to, toErr := GenerateAddress(recipient)
	if toErr != nil {
		return toErr
	}

	hdr, hdrErr := GenerateHeaders(from, to, subject, boundary)
	if hdrErr != nil {
		return hdrErr
	}
	buf.WriteString(hdr)

	body, bodyErr := GenerateBody(messageText, TEXT_MIME, boundary)
	if bodyErr != nil {
		return bodyErr
	}
	buf.WriteString(body)

	// message body in html format is optional
	if len(messageHtml) > 0 {
		htmlBody, htmlBodyErr := GenerateBody(messageHtml, HTML_MIME, boundary)
		if htmlBodyErr != nil {
			return htmlBodyErr
		}
		buf.WriteString(htmlBody)
	}

	for _, a := range attachments {
		a.Boundary = boundary
		attach, attachErr := GenerateAttachment(a)
		if attachErr != nil {
			return attachErr
		}
		buf.WriteString(attach)
	}

	// add the closing boundary marker
	buf.WriteString("\r\n--")
	buf.WriteString(boundary)
	buf.WriteString("--")

	// connect to the mail server + port
	c, cErr := smtp.Dial(fmt.Sprintf("%s:%d", server, port))
	if cErr != nil {
		return cErr
	}

	// set the sender and recipient (raw email address strings)
	c.Mail(sender.Address)
	c.Rcpt(recipient.Address)

	// stream the full email data
	wc, err := c.Data()
	if err != nil {
		return err
	}
	defer wc.Close()

	_, err = buf.WriteTo(wc)
	return err
}

// Send transmits the given message, with optional attachments, via the
// default mail server (localhost) and port (25)
func Send(subject, messageText, messageHtml string, sender, recipient *EmailAddress, attachments []*EmailAttachment) error {
	return SendFromServer(subject, messageText, messageHtml, MAIL_SERVER, sender, recipient, attachments, MAIL_PORT)
}
<|endoftext|>


<commit_before>package neurgo

import (
	"fmt"
	"github.com/ajstarks/svgo"
	"io"
	"log"
	"os"
)

type Point struct {
	x int
	y int
}

type NodeCircleSVG struct {
	Point
	radius int
}

type NodeUUIDToCircleSVG map[string]NodeCircleSVG

func (cortex *Cortex) RenderSVGFile(filename string) {
	outfile, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := outfile.Close(); err != nil {
			panic(err)
		}
	}()
	cortex.RenderSVG(outfile)
}

func (cortex *Cortex) RenderSVG(writer io.Writer) {

	width := 1000
	height := 1000
	x := 100
	xDelta := 100
	yDelta := 100
	radius := 25
	neuronFill := "fill:blue"
	actuatorFill := "fill:magenta"
	sensorFill := "fill:green"

	canvas := svg.New(writer)
	canvas.Start(width, height)
	canvas.Rect(0, 0, width, height, canvas.RGB(255, 255, 255))

	nodeUUIDToCircleSVG := make(NodeUUIDToCircleSVG)
	layerToNodeIdMap := cortex.NodeIdLayerMap()
	layerIndexes := layerToNodeIdMap.Keys()

	for _, layerIndex := range layerIndexes {

		y := 100
		nodeIds := layerToNodeIdMap[layerIndex]
		layerIndexStr := fmt.Sprintf("%v", layerIndex)

		canvas.Text(x, y, layerIndexStr, "font-size:12;fill:black")
		y += yDelta

		for _, nodeId := range nodeIds {

			switch nodeId.NodeType {
			case NEURON:
				canvas.Circle(x, y, radius, neuronFill)
			case ACTUATOR:
				canvas.Circle(x, y, radius, actuatorFill)
			case SENSOR:
				canvas.Circle(x, y, radius, sensorFill)
			}

			circleSVG := NodeCircleSVG{Point{x: x, y: y}, radius}
			nodeUUIDToCircleSVG[nodeId.UUID] = circleSVG

			y += yDelta
		}

		x += xDelta
	}

	addConnectionsToSVG(cortex, canvas, nodeUUIDToCircleSVG)

	canvas.End()

}

func addConnectionsToSVG(cortex *Cortex, canvas *svg.SVG, nodeUUIDToCircleSVG NodeUUIDToCircleSVG) {

	layerToNodeIdMap := cortex.NodeIdLayerMap()
	layerIndexes := layerToNodeIdMap.Keys()

	// loop over all layers
	for _, layerIndex := range layerIndexes {

		nodeIds := layerToNodeIdMap[layerIndex]

		// loop over all nodes
		for _, nodeId := range nodeIds {
			log.Printf("nodeId: %v", nodeId)

			// lookup node (assuming it is an OutboundConnector)
			node := cortex.FindConnector(nodeId)
			if node == nil {
				// if not, ignore it (eg, actuator)
				continue
			}

			// loop over all outbound connections
			for _, outbound := range node.outbound() {
				tgtNodeId := outbound.NodeId
				srcCircle := nodeUUIDToCircleSVG[nodeId.UUID]
				tgtCircle := nodeUUIDToCircleSVG[tgtNodeId.UUID]

				layerDelta := tgtNodeId.LayerIndex - nodeId.LayerIndex
				if layerDelta > 0 {
					adjacent := layerToNodeIdMap.LayersAdjacent(nodeId.LayerIndex, tgtNodeId.LayerIndex)
					if adjacent {
						forwardConnectNodesSVG(canvas, srcCircle, tgtCircle)
					} else {
						forwardConnectDistantNodesSVG(canvas, srcCircle, tgtCircle)
					}

				} else if layerDelta == 0 {
					selfRecurrentConnectNodesSVG(canvas, srcCircle, tgtCircle)
				} else if layerDelta < 0 {
					recurrentConnectNodesSVG(canvas, srcCircle, tgtCircle)
				}

			}

		}

	}

}

func recurrentConnectNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {

	linestyle2 := []string{`stroke="turquoise"`, `stroke-linecap="round"`, `stroke-width="5"`, `fill="none"`}
	midpoint := midpoint(Point{x: src.x, y: src.y}, Point{x: tgt.x, y: tgt.y})
	controlX := midpoint.x
	controlY := midpoint.y - 50
	canvas.Qbez(src.x, src.y, controlX, controlY, tgt.x, tgt.y, linestyle2[0], linestyle2[1], linestyle2[2], linestyle2[3])

}

func selfRecurrentConnectNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {

	linestyle2 := []string{`stroke="turquoise"`, `stroke-linecap="round"`, `stroke-width="5"`, `fill="none"`}

	srcX := src.x - 10
	srcY := src.y
	tgtX := src.x + 10
	tgtY := src.y

	controlX := src.x
	controlY := src.y - 100
	canvas.Qbez(srcX, srcY, controlX, controlY, tgtX, tgtY, linestyle2[0], linestyle2[1], linestyle2[2], linestyle2[3])

}

func midpoint(p1 Point, p2 Point) Point {
	pResult := Point{}
	pResult.x = (p1.x + p2.x) / 2
	pResult.y = (p1.y + p2.y) / 2
	return pResult
}

func forwardConnectNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {
	linestyle := []string{`stroke="black"`, `stroke-linecap="round"`, `stroke-width="5"`}

	log.Printf("line: src.x %v, src.y: %v tgt.x: %v tgt.y: %v", src.x, src.y, tgt.x, tgt.y)

	canvas.Line(src.x, src.y, tgt.x, tgt.y, linestyle[0], linestyle[1], linestyle[2])

}

func forwardConnectDistantNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {

	linestyle2 := []string{`stroke="black"`, `stroke-linecap="round"`, `stroke-width="5"`, `fill="none"`, `stroke-dasharray="10,10"`}
	midpoint := midpoint(Point{x: src.x, y: src.y}, Point{x: tgt.x, y: tgt.y})
	controlX := midpoint.x
	controlY := midpoint.y + 50
	canvas.Qbez(src.x, src.y, controlX, controlY, tgt.x, tgt.y, linestyle2[0], linestyle2[1], linestyle2[2], linestyle2[3], linestyle2[4])

}
<commit_msg>remove logging<commit_after>package neurgo

import (
	"fmt"
	"github.com/ajstarks/svgo"
	"io"
	"os"
)

type Point struct {
	x int
	y int
}

type NodeCircleSVG struct {
	Point
	radius int
}

type NodeUUIDToCircleSVG map[string]NodeCircleSVG

func (cortex *Cortex) RenderSVGFile(filename string) {
	outfile, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := outfile.Close(); err != nil {
			panic(err)
		}
	}()
	cortex.RenderSVG(outfile)
}

func (cortex *Cortex) RenderSVG(writer io.Writer) {

	width := 1000
	height := 1000
	x := 100
	xDelta := 100
	yDelta := 100
	radius := 25
	neuronFill := "fill:blue"
	actuatorFill := "fill:magenta"
	sensorFill := "fill:green"

	canvas := svg.New(writer)
	canvas.Start(width, height)
	canvas.Rect(0, 0, width, height, canvas.RGB(255, 255, 255))

	nodeUUIDToCircleSVG := make(NodeUUIDToCircleSVG)
	layerToNodeIdMap := cortex.NodeIdLayerMap()
	layerIndexes := layerToNodeIdMap.Keys()

	for _, layerIndex := range layerIndexes {

		y := 100
		nodeIds := layerToNodeIdMap[layerIndex]
		layerIndexStr := fmt.Sprintf("%v", layerIndex)

		canvas.Text(x, y, layerIndexStr, "font-size:12;fill:black")
		y += yDelta

		for _, nodeId := range nodeIds {

			switch nodeId.NodeType {
			case NEURON:
				canvas.Circle(x, y, radius, neuronFill)
			case ACTUATOR:
				canvas.Circle(x, y, radius, actuatorFill)
			case SENSOR:
				canvas.Circle(x, y, radius, sensorFill)
			}

			circleSVG := NodeCircleSVG{Point{x: x, y: y}, radius}
			nodeUUIDToCircleSVG[nodeId.UUID] = circleSVG

			y += yDelta
		}

		x += xDelta
	}

	addConnectionsToSVG(cortex, canvas, nodeUUIDToCircleSVG)

	canvas.End()

}

func addConnectionsToSVG(cortex *Cortex, canvas *svg.SVG, nodeUUIDToCircleSVG NodeUUIDToCircleSVG) {

	layerToNodeIdMap := cortex.NodeIdLayerMap()
	layerIndexes := layerToNodeIdMap.Keys()

	// loop over all layers
	for _, layerIndex := range layerIndexes {

		nodeIds := layerToNodeIdMap[layerIndex]

		// loop over all nodes
		for _, nodeId := range nodeIds {

			// lookup node (assuming it is an OutboundConnector)
			node := cortex.FindConnector(nodeId)
			if node == nil {
				// if not, ignore it (eg, actuator)
				continue
			}

			// loop over all outbound connections
			for _, outbound := range node.outbound() {
				tgtNodeId := outbound.NodeId
				srcCircle := nodeUUIDToCircleSVG[nodeId.UUID]
				tgtCircle := nodeUUIDToCircleSVG[tgtNodeId.UUID]

				layerDelta := tgtNodeId.LayerIndex - nodeId.LayerIndex
				if layerDelta > 0 {
					adjacent := layerToNodeIdMap.LayersAdjacent(nodeId.LayerIndex, tgtNodeId.LayerIndex)
					if adjacent {
						forwardConnectNodesSVG(canvas, srcCircle, tgtCircle)
					} else {
						forwardConnectDistantNodesSVG(canvas, srcCircle, tgtCircle)
					}

				} else if layerDelta == 0 {
					selfRecurrentConnectNodesSVG(canvas, srcCircle, tgtCircle)
				} else if layerDelta < 0 {
					recurrentConnectNodesSVG(canvas, srcCircle, tgtCircle)
				}

			}

		}

	}

}

func recurrentConnectNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {

	linestyle2 := []string{`stroke="turquoise"`, `stroke-linecap="round"`, `stroke-width="5"`, `fill="none"`}
	midpoint := midpoint(Point{x: src.x, y: src.y}, Point{x: tgt.x, y: tgt.y})
	controlX := midpoint.x
	controlY := midpoint.y - 50
	canvas.Qbez(src.x, src.y, controlX, controlY, tgt.x, tgt.y, linestyle2[0], linestyle2[1], linestyle2[2], linestyle2[3])

}

func selfRecurrentConnectNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {

	linestyle2 := []string{`stroke="turquoise"`, `stroke-linecap="round"`, `stroke-width="5"`, `fill="none"`}

	srcX := src.x - 10
	srcY := src.y
	tgtX := src.x + 10
	tgtY := src.y

	controlX := src.x
	controlY := src.y - 100
	canvas.Qbez(srcX, srcY, controlX, controlY, tgtX, tgtY, linestyle2[0], linestyle2[1], linestyle2[2], linestyle2[3])

}

func midpoint(p1 Point, p2 Point) Point {
	pResult := Point{}
	pResult.x = (p1.x + p2.x) / 2
	pResult.y = (p1.y + p2.y) / 2
	return pResult
}

func forwardConnectNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {
	linestyle := []string{`stroke="black"`, `stroke-linecap="round"`, `stroke-width="5"`}

	canvas.Line(src.x, src.y, tgt.x, tgt.y, linestyle[0], linestyle[1], linestyle[2])

}

func forwardConnectDistantNodesSVG(canvas *svg.SVG, src NodeCircleSVG, tgt NodeCircleSVG) {

	linestyle2 := []string{`stroke="black"`, `stroke-linecap="round"`, `stroke-width="5"`, `fill="none"`, `stroke-dasharray="10,10"`}
	midpoint := midpoint(Point{x: src.x, y: src.y}, Point{x: tgt.x, y: tgt.y})
	controlX := midpoint.x
	controlY := midpoint.y + 50
	canvas.Qbez(src.x, src.y, controlX, controlY, tgt.x, tgt.y, linestyle2[0], linestyle2[1], linestyle2[2], linestyle2[3], linestyle2[4])

}
<|endoftext|>


<commit_before>package test

import (
	"io"
	"net"
	"testing"
	"time"

	"github.com/nbusy/devastator"
	"github.com/nbusy/neptulon/jsonrpc"
)

// ClientHelper is a neptulon/jsonrpc.Client wrapper.
// All the functions are wrapped with proper test runner error logging.
type ClientHelper struct {
	client    *jsonrpc.Client
	server    *ServerHelper
	testing   *testing.T
	cert, key []byte
}

// NewClientHelper creates a new client helper object.
func NewClientHelper(t *testing.T, s *ServerHelper) *ClientHelper {
	if testing.Short() {
		t.Skip("Skipping integration test in short testing mode")
	}

	return &ClientHelper{testing: t, server: s}
}

// AsUser attaches given user's client certificate and private key to the connection.
func (c *ClientHelper) AsUser(u *devastator.User) *ClientHelper {
	c.cert = u.Cert
	c.key = u.Key
	return c
}

// WithCert attaches given PEM encoded client certificate and private key to the connection.
func (c *ClientHelper) WithCert(cert, key []byte) *ClientHelper {
	c.cert = cert
	c.key = key
	return c
}

// Dial initiates a connection.
func (c *ClientHelper) Dial() *ClientHelper {
	addr := "127.0.0.1:" + devastator.Conf.App.Port

	// retry connect in case we're operating on a very slow machine
	for i := 0; i <= 5; i++ {
		client, err := jsonrpc.Dial(addr, c.server.IntCACert, c.cert, c.key, false) // no need for debug mode on client conn as we have it on server conn already
		if err != nil {
			if operr, ok := err.(*net.OpError); ok && operr.Op == "dial" && operr.Err.Error() == "connection refused" {
				time.Sleep(time.Millisecond * 50)
				continue
			} else if i == 5 {
				c.testing.Fatalf("Cannot connect to server address %v after 5 retries, with error: %v", addr, err)
			}
			c.testing.Fatalf("Cannot connect to server address %v with error: %v", addr, err)
		}

		if i != 0 {
			c.testing.Logf("WARNING: it took %v retries to connect to the server, which might indicate code issues or slow machine.", i)
		}

		client.SetReadDeadline(10)
		c.client = client
		return c
	}

	return nil
}

// WriteRequest sends a request message through the client connection.
func (c *ClientHelper) WriteRequest(method string, params interface{}) (reqID string) {
	id, err := c.client.WriteRequest(method, params)
	if err != nil {
		c.testing.Fatal("Failed to write request to client connection:", err)
	}
	return id
}

// WriteRequestArr sends a request message through the client connection. Params object is variadic.
func (c *ClientHelper) WriteRequestArr(method string, params ...interface{}) (reqID string) {
	return c.WriteRequest(method, params)
}

// WriteNotification sends a notification message through the client connection.
func (c *ClientHelper) WriteNotification(method string, params interface{}) {
	if err := c.client.WriteNotification(method, params); err != nil {
		c.testing.Fatal("Failed to write notification to client connection:", err)
	}
}

// WriteNotificationArr sends a notification message through the client connection. Params object is variadic.
func (c *ClientHelper) WriteNotificationArr(method string, params ...interface{}) {
	c.WriteNotification(method, params)
}

// WriteResponse sends a response message through the client connection.
func (c *ClientHelper) WriteResponse(id string, result interface{}, err *jsonrpc.ResError) {
	if err := c.client.WriteResponse(id, result, err); err != nil {
		c.testing.Fatal("Failed to write response to client connection:", err)
	}
}

// ReadReq reads a request object from a client connection.
// If incoming message is not a request, a fatal error is logged.
// Optionally, you can pass in a data structure that the returned JSON-RPC request params data will be serialized into.
// Otherwise json.Unmarshal defaults apply.
func (c *ClientHelper) ReadReq(paramsData interface{}) *jsonrpc.Request {
	req, _, _, err := c.client.ReadMsg(nil, paramsData)
	if err != nil {
		c.testing.Fatal("Failed to read request from client connection:", err)
	}

	if req == nil {
		c.testing.Fatal("Read message was not a request message.")
	}

	return req
}

// ReadRes reads a response object from a client connection.
// If incoming message is not a response, a fatal error is logged.
// Optionally, you can pass in a data structure that the returned JSON-RPC response result data will be serialized into.
// Otherwise json.Unmarshal defaults apply.
func (c *ClientHelper) ReadRes(resultData interface{}) *jsonrpc.Response {
	_, res, _, err := c.client.ReadMsg(resultData, nil)
	if err != nil {
		c.testing.Fatal("Failed to read response from client connection:", err)
	}

	if res == nil {
		c.testing.Fatal("Read message was not a response message.")
	}

	return res
}

// ReadMsg reads a JSON-RPC message from a client connection. If possible, use more specific ReadReq/ReadRes/ReadNot methods instead.
// Optionally, you can pass in a data structure that the returned JSON-RPC response result data will be serialized into (same for request params).
// Otherwise json.Unmarshal defaults apply.
func (c *ClientHelper) ReadMsg(resultData interface{}, paramsData interface{}) (req *jsonrpc.Request, res *jsonrpc.Response, not *jsonrpc.Notification) {
	req, res, not, err := c.client.ReadMsg(resultData, paramsData)
	if err != nil {
		c.testing.Fatal("Failed to read message from client connection:", err)
	}

	return
}

// VerifyConnClosed verifies that the connection is in closed state.
// Verification is done via reading from the channel and checking that returned error is io.EOF.
func (c *ClientHelper) VerifyConnClosed() bool {
	_, _, _, err := c.client.ReadMsg(nil, nil)
	if err != io.EOF {
		return false
	}

	return true
}

// Close closes a client connection.
func (c *ClientHelper) Close() {
	if err := c.client.Close(); err != nil {
		c.testing.Fatal("Failed to close client connection:", err)
	}
}
<commit_msg>add doc<commit_after>package test

import (
	"io"
	"net"
	"testing"
	"time"

	"github.com/nbusy/devastator"
	"github.com/nbusy/neptulon/jsonrpc"
)

// ClientHelper is a neptulon/jsonrpc.Client wrapper.
// All the functions are wrapped with proper test runner error logging.
type ClientHelper struct {
	client    *jsonrpc.Client
	server    *ServerHelper // server that this client will be connecting to
	testing   *testing.T
	cert, key []byte
}

// NewClientHelper creates a new client helper object.
func NewClientHelper(t *testing.T, s *ServerHelper) *ClientHelper {
	if testing.Short() {
		t.Skip("Skipping integration test in short testing mode")
	}

	return &ClientHelper{testing: t, server: s}
}

// AsUser attaches given user's client certificate and private key to the connection.
func (c *ClientHelper) AsUser(u *devastator.User) *ClientHelper {
	c.cert = u.Cert
	c.key = u.Key
	return c
}

// WithCert attaches given PEM encoded client certificate and private key to the connection.
func (c *ClientHelper) WithCert(cert, key []byte) *ClientHelper {
	c.cert = cert
	c.key = key
	return c
}

// Dial initiates a connection.
func (c *ClientHelper) Dial() *ClientHelper {
	addr := "127.0.0.1:" + devastator.Conf.App.Port

	// retry connect in case we're operating on a very slow machine
	for i := 0; i <= 5; i++ {
		client, err := jsonrpc.Dial(addr, c.server.IntCACert, c.cert, c.key, false) // no need for debug mode on client conn as we have it on server conn already
		if err != nil {
			if operr, ok := err.(*net.OpError); ok && operr.Op == "dial" && operr.Err.Error() == "connection refused" {
				time.Sleep(time.Millisecond * 50)
				continue
			} else if i == 5 {
				c.testing.Fatalf("Cannot connect to server address %v after 5 retries, with error: %v", addr, err)
			}
			c.testing.Fatalf("Cannot connect to server address %v with error: %v", addr, err)
		}

		if i != 0 {
			c.testing.Logf("WARNING: it took %v retries to connect to the server, which might indicate code issues or slow machine.", i)
		}

		client.SetReadDeadline(10)
		c.client = client
		return c
	}

	return nil
}

// WriteRequest sends a request message through the client connection.
func (c *ClientHelper) WriteRequest(method string, params interface{}) (reqID string) {
	id, err := c.client.WriteRequest(method, params)
	if err != nil {
		c.testing.Fatal("Failed to write request to client connection:", err)
	}
	return id
}

// WriteRequestArr sends a request message through the client connection. Params object is variadic.
func (c *ClientHelper) WriteRequestArr(method string, params ...interface{}) (reqID string) {
	return c.WriteRequest(method, params)
}

// WriteNotification sends a notification message through the client connection.
func (c *ClientHelper) WriteNotification(method string, params interface{}) {
	if err := c.client.WriteNotification(method, params); err != nil {
		c.testing.Fatal("Failed to write notification to client connection:", err)
	}
}

// WriteNotificationArr sends a notification message through the client connection. Params object is variadic.
func (c *ClientHelper) WriteNotificationArr(method string, params ...interface{}) {
	c.WriteNotification(method, params)
}

// WriteResponse sends a response message through the client connection.
func (c *ClientHelper) WriteResponse(id string, result interface{}, err *jsonrpc.ResError) {
	if err := c.client.WriteResponse(id, result, err); err != nil {
		c.testing.Fatal("Failed to write response to client connection:", err)
	}
}

// ReadReq reads a request object from a client connection.
// If incoming message is not a request, a fatal error is logged.
// Optionally, you can pass in a data structure that the returned JSON-RPC request params data will be serialized into.
// Otherwise json.Unmarshal defaults apply.
func (c *ClientHelper) ReadReq(paramsData interface{}) *jsonrpc.Request {
	req, _, _, err := c.client.ReadMsg(nil, paramsData)
	if err != nil {
		c.testing.Fatal("Failed to read request from client connection:", err)
	}

	if req == nil {
		c.testing.Fatal("Read message was not a request message.")
	}

	return req
}

// ReadRes reads a response object from a client connection.
// If incoming message is not a response, a fatal error is logged.
// Optionally, you can pass in a data structure that the returned JSON-RPC response result data will be serialized into.
// Otherwise json.Unmarshal defaults apply.
func (c *ClientHelper) ReadRes(resultData interface{}) *jsonrpc.Response {
	_, res, _, err := c.client.ReadMsg(resultData, nil)
	if err != nil {
		c.testing.Fatal("Failed to read response from client connection:", err)
	}

	if res == nil {
		c.testing.Fatal("Read message was not a response message.")
	}

	return res
}

// ReadMsg reads a JSON-RPC message from a client connection. If possible, use more specific ReadReq/ReadRes/ReadNot methods instead.
// Optionally, you can pass in a data structure that the returned JSON-RPC response result data will be serialized into (same for request params).
// Otherwise json.Unmarshal defaults apply.
func (c *ClientHelper) ReadMsg(resultData interface{}, paramsData interface{}) (req *jsonrpc.Request, res *jsonrpc.Response, not *jsonrpc.Notification) {
	req, res, not, err := c.client.ReadMsg(resultData, paramsData)
	if err != nil {
		c.testing.Fatal("Failed to read message from client connection:", err)
	}

	return
}

// VerifyConnClosed verifies that the connection is in closed state.
// Verification is done via reading from the channel and checking that returned error is io.EOF.
func (c *ClientHelper) VerifyConnClosed() bool {
	_, _, _, err := c.client.ReadMsg(nil, nil)
	if err != io.EOF {
		return false
	}

	return true
}

// Close closes a client connection.
func (c *ClientHelper) Close() {
	if err := c.client.Close(); err != nil {
		c.testing.Fatal("Failed to close client connection:", err)
	}
}
<|endoftext|>


<commit_before>package monitor

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/rancher/norman/parse"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/clustermanager"
	monitorutil "github.com/rancher/rancher/pkg/monitoring"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
)

func NewMetricHandler(dialerFactory dialer.Factory, clustermanager *clustermanager.Manager) *MetricHandler {
	return &MetricHandler{
		dialerFactory:  dialerFactory,
		clustermanager: clustermanager,
	}
}

type MetricHandler struct {
	dialerFactory  dialer.Factory
	clustermanager *clustermanager.Manager
}

func (h *MetricHandler) Action(actionName string, action *types.Action, apiContext *types.APIContext) error {
	switch actionName {
	case querycluster, queryproject:
		var clusterName, projectName, appName, saNamespace string
		var comm v3.CommonQueryMetricInput
		var err error
		var svcNamespace, svcName string

		if actionName == querycluster {
			var queryMetricInput v3.QueryClusterMetricInput
			actionInput, err := parse.ReadBody(apiContext.Request)
			if err != nil {
				return err
			}

			if err = convert.ToObj(actionInput, &queryMetricInput); err != nil {
				return err
			}

			clusterName = queryMetricInput.ClusterName
			if clusterName == "" {
				return fmt.Errorf("clusterName is empty")
			}

			comm = queryMetricInput.CommonQueryMetricInput
			appName, saNamespace = monitorutil.ClusterMonitoringInfo()
			svcName, svcNamespace, _ = monitorutil.ClusterPrometheusEndpoint()
		} else {
			var queryMetricInput v3.QueryProjectMetricInput
			actionInput, err := parse.ReadBody(apiContext.Request)
			if err != nil {
				return err
			}
			if err = convert.ToObj(actionInput, &queryMetricInput); err != nil {
				return err
			}

			projectID := queryMetricInput.ProjectName
			clusterName, projectName = ref.Parse(projectID)

			if clusterName == "" {
				return fmt.Errorf("clusterName is empty")
			}

			comm = queryMetricInput.CommonQueryMetricInput
			appName, saNamespace = monitorutil.ProjectMonitoringInfo(projectName)
			svcName, svcNamespace = monitorutil.ProjectPrometheusServiceInfo(projectName)
		}

		start, end, step, err := parseTimeParams(comm.From, comm.To, comm.Interval)
		if err != nil {
			return err
		}

		userContext, err := h.clustermanager.UserContext(clusterName)
		if err != nil {
			return fmt.Errorf("get usercontext failed, %v", err)
		}

		token, err := getAuthToken(userContext, appName, saNamespace)
		if err != nil {
			return err
		}

		reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
		defer cancel()

		prometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, svcNamespace, svcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}

		query := InitPromQuery("", start, end, step, comm.Expr, "", false)
		seriesSlice, err := prometheusQuery.QueryRange(query)
		if err != nil {
			return err
		}

		if seriesSlice == nil {
			apiContext.WriteResponse(http.StatusNoContent, nil)
			return nil
		}

		data := map[string]interface{}{
			"type":   "queryMetricOutput",
			"series": seriesSlice,
		}

		res, err := json.Marshal(data)
		if err != nil {
			return fmt.Errorf("marshal query stats result failed, %v", err)
		}
		apiContext.Response.Write(res)

	case listclustermetricname:
		var input v3.ClusterMetricNamesInput
		actionInput, err := parse.ReadBody(apiContext.Request)
		if err != nil {
			return err
		}
		if err = convert.ToObj(actionInput, &input); err != nil {
			return err
		}

		clusterName := input.ClusterName
		if clusterName == "" {
			return fmt.Errorf("clusterName is empty")
		}

		userContext, err := h.clustermanager.UserContext(clusterName)
		if err != nil {
			return fmt.Errorf("get usercontext failed, %v", err)
		}

		appName, saNamespace := monitorutil.ClusterMonitoringInfo()
		token, err := getAuthToken(userContext, appName, saNamespace)
		if err != nil {
			return err
		}

		reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
		defer cancel()

		svcName, svcNamespace, _ := monitorutil.ClusterPrometheusEndpoint()
		prometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, svcNamespace, svcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}

		names, err := prometheusQuery.GetLabelValues("__name__")
		if err != nil {
			return err
		}
		data := map[string]interface{}{
			"type":  "metricNamesOutput",
			"names": names,
		}

		apiContext.WriteResponse(http.StatusOK, data)
	case listprojectmetricname:
		// project metric names need to merge cluster level and project level prometheus labels name list
		var input v3.ProjectMetricNamesInput
		actionInput, err := parse.ReadBody(apiContext.Request)
		if err != nil {
			return err
		}
		if err = convert.ToObj(actionInput, &input); err != nil {
			return err
		}

		projectID := input.ProjectName
		clusterName, projectName := ref.Parse(projectID)

		if clusterName == "" {
			return fmt.Errorf("clusterName is empty")
		}

		userContext, err := h.clustermanager.UserContext(clusterName)
		if err != nil {
			return fmt.Errorf("get usercontext failed, %v", err)
		}

		appName, saNamespace := monitorutil.ProjectMonitoringInfo(projectName)
		token, err := getAuthToken(userContext, appName, saNamespace)
		if err != nil {
			return err
		}

		reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
		defer cancel()

		// get name list for user definded metric from project prometheus
		projectSvcName, projectSvcNamespace := monitorutil.ProjectPrometheusServiceInfo(projectName)
		projectPrometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, projectSvcNamespace, projectSvcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}
		projectNames, err := projectPrometheusQuery.GetLabelValues("__name__")
		if err != nil {
			return fmt.Errorf("get project metric list failed, %v", err)
		}

		// get name list for pod/container metric from cluster prometheus
		clusterSvcName, clusterSvcNamespace, _ := monitorutil.ClusterPrometheusEndpoint()
		clusterPrometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, clusterSvcNamespace, clusterSvcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}

		clusterNames, err := clusterPrometheusQuery.GetLabelValues("__name__")
		if err != nil {
			return fmt.Errorf("get cluster metric list failed, %v", err)
		}

		names := append(projectNames, clusterNames...)
		data := map[string]interface{}{
			"type":  "metricNamesOutput",
			"names": names,
		}
		apiContext.WriteResponse(http.StatusOK, data)
	}
	return nil

}
<commit_msg>Fix duplicate prompt expressions when operating alert rule<commit_after>package monitor

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/rancher/norman/parse"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/clustermanager"
	monitorutil "github.com/rancher/rancher/pkg/monitoring"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/util/sets"
)

func NewMetricHandler(dialerFactory dialer.Factory, clustermanager *clustermanager.Manager) *MetricHandler {
	return &MetricHandler{
		dialerFactory:  dialerFactory,
		clustermanager: clustermanager,
	}
}

type MetricHandler struct {
	dialerFactory  dialer.Factory
	clustermanager *clustermanager.Manager
}

func (h *MetricHandler) Action(actionName string, action *types.Action, apiContext *types.APIContext) error {
	switch actionName {
	case querycluster, queryproject:
		var clusterName, projectName, appName, saNamespace string
		var comm v3.CommonQueryMetricInput
		var err error
		var svcNamespace, svcName string

		if actionName == querycluster {
			var queryMetricInput v3.QueryClusterMetricInput
			actionInput, err := parse.ReadBody(apiContext.Request)
			if err != nil {
				return err
			}

			if err = convert.ToObj(actionInput, &queryMetricInput); err != nil {
				return err
			}

			clusterName = queryMetricInput.ClusterName
			if clusterName == "" {
				return fmt.Errorf("clusterName is empty")
			}

			comm = queryMetricInput.CommonQueryMetricInput
			appName, saNamespace = monitorutil.ClusterMonitoringInfo()
			svcName, svcNamespace, _ = monitorutil.ClusterPrometheusEndpoint()
		} else {
			var queryMetricInput v3.QueryProjectMetricInput
			actionInput, err := parse.ReadBody(apiContext.Request)
			if err != nil {
				return err
			}
			if err = convert.ToObj(actionInput, &queryMetricInput); err != nil {
				return err
			}

			projectID := queryMetricInput.ProjectName
			clusterName, projectName = ref.Parse(projectID)

			if clusterName == "" {
				return fmt.Errorf("clusterName is empty")
			}

			comm = queryMetricInput.CommonQueryMetricInput
			appName, saNamespace = monitorutil.ProjectMonitoringInfo(projectName)
			svcName, svcNamespace = monitorutil.ProjectPrometheusServiceInfo(projectName)
		}

		start, end, step, err := parseTimeParams(comm.From, comm.To, comm.Interval)
		if err != nil {
			return err
		}

		userContext, err := h.clustermanager.UserContext(clusterName)
		if err != nil {
			return fmt.Errorf("get usercontext failed, %v", err)
		}

		token, err := getAuthToken(userContext, appName, saNamespace)
		if err != nil {
			return err
		}

		reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
		defer cancel()

		prometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, svcNamespace, svcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}

		query := InitPromQuery("", start, end, step, comm.Expr, "", false)
		seriesSlice, err := prometheusQuery.QueryRange(query)
		if err != nil {
			return err
		}

		if seriesSlice == nil {
			apiContext.WriteResponse(http.StatusNoContent, nil)
			return nil
		}

		data := map[string]interface{}{
			"type":   "queryMetricOutput",
			"series": seriesSlice,
		}

		res, err := json.Marshal(data)
		if err != nil {
			return fmt.Errorf("marshal query stats result failed, %v", err)
		}
		apiContext.Response.Write(res)

	case listclustermetricname:
		var input v3.ClusterMetricNamesInput
		actionInput, err := parse.ReadBody(apiContext.Request)
		if err != nil {
			return err
		}
		if err = convert.ToObj(actionInput, &input); err != nil {
			return err
		}

		clusterName := input.ClusterName
		if clusterName == "" {
			return fmt.Errorf("clusterName is empty")
		}

		userContext, err := h.clustermanager.UserContext(clusterName)
		if err != nil {
			return fmt.Errorf("get usercontext failed, %v", err)
		}

		appName, saNamespace := monitorutil.ClusterMonitoringInfo()
		token, err := getAuthToken(userContext, appName, saNamespace)
		if err != nil {
			return err
		}

		reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
		defer cancel()

		svcName, svcNamespace, _ := monitorutil.ClusterPrometheusEndpoint()
		prometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, svcNamespace, svcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}

		names, err := prometheusQuery.GetLabelValues("__name__")
		if err != nil {
			return err
		}
		data := map[string]interface{}{
			"type":  "metricNamesOutput",
			"names": names,
		}

		apiContext.WriteResponse(http.StatusOK, data)
	case listprojectmetricname:
		// project metric names need to merge cluster level and project level prometheus labels name list
		var input v3.ProjectMetricNamesInput
		actionInput, err := parse.ReadBody(apiContext.Request)
		if err != nil {
			return err
		}
		if err = convert.ToObj(actionInput, &input); err != nil {
			return err
		}

		projectID := input.ProjectName
		clusterName, projectName := ref.Parse(projectID)

		if clusterName == "" {
			return fmt.Errorf("clusterName is empty")
		}

		userContext, err := h.clustermanager.UserContext(clusterName)
		if err != nil {
			return fmt.Errorf("get usercontext failed, %v", err)
		}

		appName, saNamespace := monitorutil.ProjectMonitoringInfo(projectName)
		token, err := getAuthToken(userContext, appName, saNamespace)
		if err != nil {
			return err
		}

		reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
		defer cancel()

		// get name list for user definded metric from project prometheus
		projectSvcName, projectSvcNamespace := monitorutil.ProjectPrometheusServiceInfo(projectName)
		projectPrometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, projectSvcNamespace, projectSvcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}
		projectNames, err := projectPrometheusQuery.GetLabelValues("__name__")
		if err != nil {
			return fmt.Errorf("get project metric list failed, %v", err)
		}

		// get name list for pod/container metric from cluster prometheus
		clusterSvcName, clusterSvcNamespace, _ := monitorutil.ClusterPrometheusEndpoint()
		clusterPrometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, clusterSvcNamespace, clusterSvcName, h.clustermanager, h.dialerFactory)
		if err != nil {
			return err
		}

		clusterNames, err := clusterPrometheusQuery.GetLabelValues("__name__")
		if err != nil {
			return fmt.Errorf("get cluster metric list failed, %v", err)
		}

		names := sets.String{}
		names.Insert(projectNames...)
		names.Insert(clusterNames...)

		data := map[string]interface{}{
			"type":  "metricNamesOutput",
			"names": names.List(),
		}
		apiContext.WriteResponse(http.StatusOK, data)
	}
	return nil

}
<|endoftext|>


<commit_before>// +build windows

package liveexec

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/onsi/ginkgo"
)

func doExecCommand(dir, binPath string, opts ExecCommandOptions, arg ...string) error {
	cmd := exec.Command(binPath, arg...)
	cmd.Dir = dir

	cmd.Env = os.Environ()
	for k, v := range opts.Env {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
	}

	absDir, _ := filepath.Abs(dir)
	_, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "\r\n[DEBUG] COMMAND in %s: %s %s\r\n\r\n", absDir, binPath, strings.Join(arg, " "))

	res, err := cmd.CombinedOutput()
	lineBuf := make([]byte, 0, 4096)
	for _, b := range res {
		if b == '\n' {
			line := string(lineBuf)
			lineBuf = lineBuf[:0]

			_, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "[DEBUG] OUTPUT LINE: %s\r\n", line)

			if opts.OutputLineHandler != nil {
				opts.OutputLineHandler(line)
			}

			continue
		}

		lineBuf = append(lineBuf, b)
	}

	if err != nil {
		return fmt.Errorf("command failed: %s\r\n%s", err, res)
	}
	return nil
}
<commit_msg>[tests] Fix liveexec error handling on windows<commit_after>// +build windows

package liveexec

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/onsi/ginkgo"
)

func doExecCommand(dir, binPath string, opts ExecCommandOptions, arg ...string) error {
	cmd := exec.Command(binPath, arg...)
	cmd.Dir = dir

	cmd.Env = os.Environ()
	for k, v := range opts.Env {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
	}

	absDir, _ := filepath.Abs(dir)
	_, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "\r\n[DEBUG] COMMAND in %s: %s %s\r\n\r\n", absDir, binPath, strings.Join(arg, " "))

	res, err := cmd.CombinedOutput()
	lineBuf := make([]byte, 0, 4096)
	for _, b := range res {
		if b == '\n' {
			line := string(lineBuf)
			lineBuf = lineBuf[:0]

			_, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "[DEBUG] OUTPUT LINE: %s\r\n", line)

			if opts.OutputLineHandler != nil {
				opts.OutputLineHandler(line)
			}

			continue
		}

		lineBuf = append(lineBuf, b)
	}

	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			return fmt.Errorf("exit code %d", exitError.ExitCode())
		}
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// +build darwin

package xdg_test

import (
	"path/filepath"
	"testing"

	"github.com/adrg/xdg"
)

func TestDefaultBaseDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DATA_HOME",
			expected: filepath.Join(home, "Library", "Application Support"),
			actual:   &xdg.DataHome,
		},
		&envSample{
			name:     "XDG_DATA_DIRS",
			expected: []string{"/Library/Application Support"},
			actual:   &xdg.DataDirs,
		},
		&envSample{
			name:     "XDG_CONFIG_HOME",
			expected: filepath.Join(home, "Library", "Preferences"),
			actual:   &xdg.ConfigHome,
		},
		&envSample{
			name:     "XDG_CONFIG_DIRS",
			expected: []string{"/Library/Preferences"},
			actual:   &xdg.ConfigDirs,
		},
		&envSample{
			name:     "XDG_CACHE_HOME",
			expected: filepath.Join(home, "Library", "Caches"),
			actual:   &xdg.CacheHome,
		},
		&envSample{
			name:     "XDG_RUNTIME_DIR",
			expected: filepath.Join(home, "Library", "Application Support"),
			actual:   &xdg.RuntimeDir,
		},
		&envSample{
			name:     "XDG_STATE_HOME",
			expected: filepath.Join(home, "Library", "Application Support"),
			actual:   &xdg.StateHome,
		},
		&envSample{
			name: "XDG_APPLICATION_DIRS",
			expected: []string{
				"/Applications",
			},
			actual: &xdg.ApplicationDirs,
		},
		&envSample{
			name: "XDG_FONT_DIRS",
			expected: []string{
				filepath.Join(home, "Library/Fonts"),
				"/Library/Fonts",
				"/System/Library/Fonts",
				"/Network/Library/Fonts",
			},
			actual: &xdg.FontDirs,
		},
	)
}

func TestCustomBaseDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DATA_HOME",
			value:    "~/Library/data",
			expected: filepath.Join(home, "Library/data"),
			actual:   &xdg.DataHome,
		},
		&envSample{
			name:     "XDG_DATA_DIRS",
			value:    "~/Library/data:/Library/Application Support",
			expected: []string{filepath.Join(home, "Library/data"), "/Library/Application Support"},
			actual:   &xdg.DataDirs,
		},
		&envSample{
			name:     "XDG_CONFIG_HOME",
			value:    "~/Library/config",
			expected: filepath.Join(home, "Library/config"),
			actual:   &xdg.ConfigHome,
		},
		&envSample{
			name:     "XDG_CONFIG_DIRS",
			value:    "~/Library/config:/Library/Preferences",
			expected: []string{filepath.Join(home, "Library/config"), "/Library/Preferences"},
			actual:   &xdg.ConfigDirs,
		},
		&envSample{
			name:     "XDG_CACHE_HOME",
			value:    "~/Library/cache",
			expected: filepath.Join(home, "Library/cache"),
			actual:   &xdg.CacheHome,
		},
		&envSample{
			name:     "XDG_RUNTIME_DIR",
			value:    "~/Library/runtime",
			expected: filepath.Join(home, "Library/runtime"),
			actual:   &xdg.RuntimeDir,
		},
		&envSample{
			name:     "XDG_STATE_HOME",
			value:    "~/Library/state",
			expected: filepath.Join(home, "Library/state"),
			actual:   &xdg.StateHome,
		},
	)
}

func TestDefaultUserDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DESKTOP_DIR",
			expected: filepath.Join(home, "Desktop"),
			actual:   &xdg.UserDirs.Desktop,
		},
		&envSample{
			name:     "XDG_DOWNLOAD_DIR",
			expected: filepath.Join(home, "Downloads"),
			actual:   &xdg.UserDirs.Download,
		},
		&envSample{
			name:     "XDG_DOCUMENTS_DIR",
			expected: filepath.Join(home, "Documents"),
			actual:   &xdg.UserDirs.Documents,
		},
		&envSample{
			name:     "XDG_MUSIC_DIR",
			expected: filepath.Join(home, "Music"),
			actual:   &xdg.UserDirs.Music,
		},
		&envSample{
			name:     "XDG_PICTURES_DIR",
			expected: filepath.Join(home, "Pictures"),
			actual:   &xdg.UserDirs.Pictures,
		},
		&envSample{
			name:     "XDG_VIDEOS_DIR",
			expected: filepath.Join(home, "Movies"),
			actual:   &xdg.UserDirs.Videos,
		},
		&envSample{
			name:     "XDG_TEMPLATES_DIR",
			expected: filepath.Join(home, "Templates"),
			actual:   &xdg.UserDirs.Templates,
		},
		&envSample{
			name:     "XDG_PUBLICSHARE_DIR",
			expected: filepath.Join(home, "Public"),
			actual:   &xdg.UserDirs.PublicShare,
		},
	)
}

func TestCustomUserDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DESKTOP_DIR",
			value:    "$HOME/Library/Desktop",
			expected: filepath.Join(home, "Library/Desktop"),
			actual:   &xdg.UserDirs.Desktop,
		},
		&envSample{
			name:     "XDG_DOWNLOAD_DIR",
			value:    "$HOME/Library/Downloads",
			expected: filepath.Join(home, "Library/Downloads"),
			actual:   &xdg.UserDirs.Download,
		},
		&envSample{
			name:     "XDG_DOCUMENTS_DIR",
			value:    "$HOME/Library/Documents",
			expected: filepath.Join(home, "Library/Documents"),
			actual:   &xdg.UserDirs.Documents,
		},
		&envSample{
			name:     "XDG_MUSIC_DIR",
			value:    "$HOME/Library/Music",
			expected: filepath.Join(home, "Library/Music"),
			actual:   &xdg.UserDirs.Music,
		},
		&envSample{
			name:     "XDG_PICTURES_DIR",
			value:    "$HOME/Library/Pictures",
			expected: filepath.Join(home, "Library/Pictures"),
			actual:   &xdg.UserDirs.Pictures,
		},
		&envSample{
			name:     "XDG_VIDEOS_DIR",
			value:    "$HOME/Library/Movies",
			expected: filepath.Join(home, "Library/Movies"),
			actual:   &xdg.UserDirs.Videos,
		},
		&envSample{
			name:     "XDG_TEMPLATES_DIR",
			value:    "$HOME/Library/Templates",
			expected: filepath.Join(home, "Library/Templates"),
			actual:   &xdg.UserDirs.Templates,
		},
		&envSample{
			name:     "XDG_PUBLICSHARE_DIR",
			value:    "$HOME/Library/Public",
			expected: filepath.Join(home, "Library/Public"),
			actual:   &xdg.UserDirs.PublicShare,
		},
	)
}
<commit_msg>Update default base directories test case for macOS<commit_after>// +build darwin

package xdg_test

import (
	"path/filepath"
	"testing"

	"github.com/adrg/xdg"
)

func TestDefaultBaseDirs(t *testing.T) {
	home := xdg.Home
	homeAppSupport := filepath.Join(home, "Library", "Application Support")
	rootAppSupport := "/Library/Application Support"

	testDirs(t,
		&envSample{
			name:     "XDG_DATA_HOME",
			expected: homeAppSupport,
			actual:   &xdg.DataHome,
		},
		&envSample{
			name:     "XDG_DATA_DIRS",
			expected: []string{rootAppSupport},
			actual:   &xdg.DataDirs,
		},
		&envSample{
			name:     "XDG_CONFIG_HOME",
			expected: homeAppSupport,
			actual:   &xdg.ConfigHome,
		},
		&envSample{
			name: "XDG_CONFIG_DIRS",
			expected: []string{
				filepath.Join(home, "Library", "Preferences"),
				rootAppSupport,
				"/Library/Preferences",
			},
			actual: &xdg.ConfigDirs,
		},
		&envSample{
			name:     "XDG_CACHE_HOME",
			expected: filepath.Join(home, "Library", "Caches"),
			actual:   &xdg.CacheHome,
		},
		&envSample{
			name:     "XDG_RUNTIME_DIR",
			expected: homeAppSupport,
			actual:   &xdg.RuntimeDir,
		},
		&envSample{
			name:     "XDG_STATE_HOME",
			expected: homeAppSupport,
			actual:   &xdg.StateHome,
		},
		&envSample{
			name: "XDG_APPLICATION_DIRS",
			expected: []string{
				"/Applications",
			},
			actual: &xdg.ApplicationDirs,
		},
		&envSample{
			name: "XDG_FONT_DIRS",
			expected: []string{
				filepath.Join(home, "Library/Fonts"),
				"/Library/Fonts",
				"/System/Library/Fonts",
				"/Network/Library/Fonts",
			},
			actual: &xdg.FontDirs,
		},
	)
}

func TestCustomBaseDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DATA_HOME",
			value:    "~/Library/data",
			expected: filepath.Join(home, "Library/data"),
			actual:   &xdg.DataHome,
		},
		&envSample{
			name:     "XDG_DATA_DIRS",
			value:    "~/Library/data:/Library/Application Support",
			expected: []string{filepath.Join(home, "Library/data"), "/Library/Application Support"},
			actual:   &xdg.DataDirs,
		},
		&envSample{
			name:     "XDG_CONFIG_HOME",
			value:    "~/Library/config",
			expected: filepath.Join(home, "Library/config"),
			actual:   &xdg.ConfigHome,
		},
		&envSample{
			name:     "XDG_CONFIG_DIRS",
			value:    "~/Library/config:/Library/Preferences",
			expected: []string{filepath.Join(home, "Library/config"), "/Library/Preferences"},
			actual:   &xdg.ConfigDirs,
		},
		&envSample{
			name:     "XDG_CACHE_HOME",
			value:    "~/Library/cache",
			expected: filepath.Join(home, "Library/cache"),
			actual:   &xdg.CacheHome,
		},
		&envSample{
			name:     "XDG_RUNTIME_DIR",
			value:    "~/Library/runtime",
			expected: filepath.Join(home, "Library/runtime"),
			actual:   &xdg.RuntimeDir,
		},
		&envSample{
			name:     "XDG_STATE_HOME",
			value:    "~/Library/state",
			expected: filepath.Join(home, "Library/state"),
			actual:   &xdg.StateHome,
		},
	)
}

func TestDefaultUserDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DESKTOP_DIR",
			expected: filepath.Join(home, "Desktop"),
			actual:   &xdg.UserDirs.Desktop,
		},
		&envSample{
			name:     "XDG_DOWNLOAD_DIR",
			expected: filepath.Join(home, "Downloads"),
			actual:   &xdg.UserDirs.Download,
		},
		&envSample{
			name:     "XDG_DOCUMENTS_DIR",
			expected: filepath.Join(home, "Documents"),
			actual:   &xdg.UserDirs.Documents,
		},
		&envSample{
			name:     "XDG_MUSIC_DIR",
			expected: filepath.Join(home, "Music"),
			actual:   &xdg.UserDirs.Music,
		},
		&envSample{
			name:     "XDG_PICTURES_DIR",
			expected: filepath.Join(home, "Pictures"),
			actual:   &xdg.UserDirs.Pictures,
		},
		&envSample{
			name:     "XDG_VIDEOS_DIR",
			expected: filepath.Join(home, "Movies"),
			actual:   &xdg.UserDirs.Videos,
		},
		&envSample{
			name:     "XDG_TEMPLATES_DIR",
			expected: filepath.Join(home, "Templates"),
			actual:   &xdg.UserDirs.Templates,
		},
		&envSample{
			name:     "XDG_PUBLICSHARE_DIR",
			expected: filepath.Join(home, "Public"),
			actual:   &xdg.UserDirs.PublicShare,
		},
	)
}

func TestCustomUserDirs(t *testing.T) {
	home := xdg.Home

	testDirs(t,
		&envSample{
			name:     "XDG_DESKTOP_DIR",
			value:    "$HOME/Library/Desktop",
			expected: filepath.Join(home, "Library/Desktop"),
			actual:   &xdg.UserDirs.Desktop,
		},
		&envSample{
			name:     "XDG_DOWNLOAD_DIR",
			value:    "$HOME/Library/Downloads",
			expected: filepath.Join(home, "Library/Downloads"),
			actual:   &xdg.UserDirs.Download,
		},
		&envSample{
			name:     "XDG_DOCUMENTS_DIR",
			value:    "$HOME/Library/Documents",
			expected: filepath.Join(home, "Library/Documents"),
			actual:   &xdg.UserDirs.Documents,
		},
		&envSample{
			name:     "XDG_MUSIC_DIR",
			value:    "$HOME/Library/Music",
			expected: filepath.Join(home, "Library/Music"),
			actual:   &xdg.UserDirs.Music,
		},
		&envSample{
			name:     "XDG_PICTURES_DIR",
			value:    "$HOME/Library/Pictures",
			expected: filepath.Join(home, "Library/Pictures"),
			actual:   &xdg.UserDirs.Pictures,
		},
		&envSample{
			name:     "XDG_VIDEOS_DIR",
			value:    "$HOME/Library/Movies",
			expected: filepath.Join(home, "Library/Movies"),
			actual:   &xdg.UserDirs.Videos,
		},
		&envSample{
			name:     "XDG_TEMPLATES_DIR",
			value:    "$HOME/Library/Templates",
			expected: filepath.Join(home, "Library/Templates"),
			actual:   &xdg.UserDirs.Templates,
		},
		&envSample{
			name:     "XDG_PUBLICSHARE_DIR",
			value:    "$HOME/Library/Public",
			expected: filepath.Join(home, "Library/Public"),
			actual:   &xdg.UserDirs.PublicShare,
		},
	)
}
<|endoftext|>