<commit_before>/*
	Staff directory pilot API
	sits along side neo4j server serving up requests
*/

package main

import (
	"github.com/marcom-unimelb/ssdir/staffdir"
	// "encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/codegangsta/martini"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	// "time"
)

const (
	ENDPOINT   = "http://localhost:7474/db/data"
	PERSON     = "Val"
	EMAIL      = "tania.elliott@unimelb.edu.au"
	DEPARTMENT = "ITS"
	PHONE      = "7966"
)

// log to file
func LogFile(message string) {
	f, err := os.OpenFile("staffdir_access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Printf("error opening file: %v", err)
	}
	defer f.Close()

	log.SetOutput(f)
	log.Println(message)
}

// Convert returned neo4j results to structs
func ProcessResults(t interface{}) []interface{} {
	z := reflect.ValueOf(t).Elem()
	s := make([]interface{}, z.Len())
	for i := 0; i < z.Len(); i++ {
		s[i] = z.Index(i).Interface()
	}
	// for _, n := range s {
	//  temp, _ := json.Marshal(n.(PersonSummary))
	//  fmt.Println(string(temp))
	// }
	return s
}

// Main function for the API, starts up martini instance
func main() {
	m := martini.Classic()
	fmt.Println("Initialising")
	db := new(staffdir.Database)
	db.Connect(ENDPOINT)

	m.Get("/staffdir/department", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block, _ := url.QueryUnescape(strings.SplitAfter(r.RequestURI, "/?")[1])
		fmt.Println(block)
		results, ok := db.SearchDepartment(block)
		if ok != nil {
			log.Fatalln("issue with results")
		}
		out := ProcessResults(results)
		fmt.Println(out)
		var temp []byte
		var tempOut []staffdir.PersonSummary
		if len(out) > 0 {
			for _, b := range out {
				tempOut = append(tempOut, b.(staffdir.PersonSummary))
			}
			temp, _ = json.Marshal(tempOut)
			//temp, _ := json.Marshal(out[0].(staffdir.PersonSummary))
		}
		return 1, nil  // string(temp)
		// var data []string
		// if strings.Contains(block, "%7C") {
		// 	data = strings.Split(strings.SplitAfter(r.RequestURI, "/?")[1], "|")
		// } else {
		// 	data = strings.Split(strings.SplitAfter(r.RequestURI, "/?")[1], "|")
		// }
		// for a, b := range data {
		// 	log.Println("d: " + b)
		// }
		// output := EncodeJWT(GenDict(data))
		// ENDPOINT := "https://unimelbit.zendesk.com/access/jwt?jwt="
		// res.Header().Set("Location", (ENDPOINT + output))
	})

	// process authentication
	m.Get("/staffdir/person", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block, _ := url.QueryUnescape(strings.SplitAfter(r.RequestURI, "/?")[1])
		fmt.Println(block)
		results, ok := db.SearchPeople(block)
		if ok != nil {
			log.Fatalln("issue with results")
		}
		out := ProcessResults(results)
		fmt.Println(out)
		var temp []byte
		var tempOut []staffdir.PersonSummary
		if len(out) > 0 {
			for _, b := range out {
				tempOut = append(tempOut, b.(staffdir.PersonSummary))
			}
			temp, _ = json.Marshal(tempOut)
			//temp, _ := json.Marshal(out[0].(staffdir.PersonSummary))
		}
		return string(temp)
	})

	m.Get("/staffdir/manager", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block := strings.SplitAfter(r.RequestURI, "/?")[1]

		return 301, block
	})

	m.Get("/staffdir/colleagues", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block := strings.SplitAfter(r.RequestURI, "/?")[1]
		return 301, block
	})

	m.Get("/staffdir/reports", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block := strings.SplitAfter(r.RequestURI, "/?")[1]

		return 301, block
	})

	m.Patch("/", func() {
		// update something
	})

	// m.Post("/staffdir/ticket", func(r *http.Request, res http.ResponseWriter) (int, string) {
	// 	ENDPOINT := "https://staff.unimelb.edu.au/its/requests/index#/thankyou/"
	// 	if err := r.ParseForm(); err != nil {
	// 		log.Printf("%s", "nothing posted")
	// 	}
	// 	data_values := make(map[string]string)
	// 	for a, b := range r.Form {
	// 		data_values[a] = b[0]
	// 	}
	// 	output := SubmitTicket(data_values)
	// 	//res.Header().Set("Content-Type", "application/json")
	// 	// SubmitTicket(data_values)
	// 	res.Header().Set("Location", (ENDPOINT + output))
	// 	//return "<meta http-equiv=\"refresh\" content=\"0;URL=" + (ENDPOINT + output) + "\">"
	// 	return 301, ""
	// })

	m.Put("/", func() {
		// replace something
	})

	m.Delete("/", func() {
		// destroy something
	})

	m.Options("/", func() {
		// http options
	})

	m.NotFound(func() string {
		// handle 404
		log.Printf("%s\n", "Yep...")
		return "Something went wrong."
	})

	//m.Run()
	err := http.ListenAndServe(":5001", m)
	//err := http.ListenAndServeTLS(":8080", "/home/wep/tls/certs/star.its.unimelb.edu.au.chain.crt", "/home/wep/tls/private/star.its.unimelb.edu.au.key", m)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>updated api functions<commit_after>/*
	Staff directory pilot API
	sits along side neo4j server serving up requests
*/

package main

import (
	"github.com/marcom-unimelb/ssdir/staffdir"
	// "encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/codegangsta/martini"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	// "time"
)

const (
	ENDPOINT   = "http://localhost:7474/db/data"
	PERSON     = "Val"
	EMAIL      = "tania.elliott@unimelb.edu.au"
	DEPARTMENT = "ITS"
	PHONE      = "7966"
)

// log to file
func LogFile(message string) {
	f, err := os.OpenFile("staffdir_access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Printf("error opening file: %v", err)
	}
	defer f.Close()

	log.SetOutput(f)
	log.Println(message)
}

// Convert returned neo4j results to structs
func ProcessResults(t interface{}) []interface{} {
	z := reflect.ValueOf(t).Elem()
	s := make([]interface{}, z.Len())
	for i := 0; i < z.Len(); i++ {
		s[i] = z.Index(i).Interface()
	}
	// for _, n := range s {
	//  temp, _ := json.Marshal(n.(PersonSummary))
	//  fmt.Println(string(temp))
	// }
	return s
}

// Main function for the API, starts up martini instance
func main() {
	m := martini.Classic()
	fmt.Println("Initialising")
	db := new(staffdir.Database)
	db.Connect(ENDPOINT)

	m.Get("/staffdir/department", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block, _ := url.QueryUnescape(strings.SplitAfter(r.RequestURI, "/?")[1])
		fmt.Println(block)
		results, ok := db.SearchDepartment(block)
		if ok != nil {
			log.Fatalln("issue with results")
		}
		out := ProcessResults(results)
		fmt.Println(out)
		var temp []byte
		var tempOut []staffdir.PersonSummary
		if len(out) > 0 {
			for _, b := range out {
				tempOut = append(tempOut, b.(staffdir.PersonSummary))
			}
			temp, _ = json.Marshal(tempOut)
			//temp, _ := json.Marshal(out[0].(staffdir.PersonSummary))
		}
		return 200, string(temp)
		// var data []string
		// if strings.Contains(block, "%7C") {
		// 	data = strings.Split(strings.SplitAfter(r.RequestURI, "/?")[1], "|")
		// } else {
		// 	data = strings.Split(strings.SplitAfter(r.RequestURI, "/?")[1], "|")
		// }
		// for a, b := range data {
		// 	log.Println("d: " + b)
		// }
		// output := EncodeJWT(GenDict(data))
		// ENDPOINT := "https://unimelbit.zendesk.com/access/jwt?jwt="
		// res.Header().Set("Location", (ENDPOINT + output))
	})

	// process authentication
	m.Get("/staffdir/person", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block, _ := url.QueryUnescape(strings.SplitAfter(r.RequestURI, "/?")[1])
		fmt.Println(block)
		results, ok := db.SearchPeople(block)
		if ok != nil {
			log.Fatalln("issue with results")
		}
		out := ProcessResults(results)
		fmt.Println(out)
		var temp []byte
		var tempOut []staffdir.PersonSummary
		if len(out) > 0 {
			for _, b := range out {
				tempOut = append(tempOut, b.(staffdir.PersonSummary))
			}
			temp, _ = json.Marshal(tempOut)
			//temp, _ := json.Marshal(out[0].(staffdir.PersonSummary))
		}
		return 200, string(temp)
	})

	m.Get("/staffdir/manager", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block := strings.SplitAfter(r.RequestURI, "/?")[1]

		return 301, block
	})

	m.Get("/staffdir/colleagues", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block := strings.SplitAfter(r.RequestURI, "/?")[1]
		return 301, block
	})

	m.Get("/staffdir/reports", func(res http.ResponseWriter, r *http.Request) (int, string) {
		block := strings.SplitAfter(r.RequestURI, "/?")[1]

		return 301, block
	})

	m.Patch("/", func() {
		// update something
	})

	// m.Post("/staffdir/ticket", func(r *http.Request, res http.ResponseWriter) (int, string) {
	// 	ENDPOINT := "https://staff.unimelb.edu.au/its/requests/index#/thankyou/"
	// 	if err := r.ParseForm(); err != nil {
	// 		log.Printf("%s", "nothing posted")
	// 	}
	// 	data_values := make(map[string]string)
	// 	for a, b := range r.Form {
	// 		data_values[a] = b[0]
	// 	}
	// 	output := SubmitTicket(data_values)
	// 	//res.Header().Set("Content-Type", "application/json")
	// 	// SubmitTicket(data_values)
	// 	res.Header().Set("Location", (ENDPOINT + output))
	// 	//return "<meta http-equiv=\"refresh\" content=\"0;URL=" + (ENDPOINT + output) + "\">"
	// 	return 301, ""
	// })

	m.Put("/", func() {
		// replace something
	})

	m.Delete("/", func() {
		// destroy something
	})

	m.Options("/", func() {
		// http options
	})

	m.NotFound(func() string {
		// handle 404
		log.Printf("%s\n", "Yep...")
		return "Something went wrong."
	})

	//m.Run()
	err := http.ListenAndServe(":5001", m)
	//err := http.ListenAndServeTLS(":8080", "/home/wep/tls/certs/star.its.unimelb.edu.au.chain.crt", "/home/wep/tls/private/star.its.unimelb.edu.au.key", m)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package geocodio

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// Call uses basic (GET) method to make a request to the API
func (g *Geocodio) Call(path string, query map[string]string) (GeocodeResult, error) {

	if strings.Index(path, "/") != 0 {
		return GeocodeResult{}, errors.New("Path must start with a forward slash: ' / ' ")
	}

	_url := GeocodioAPIBaseURLv1 + path + "?api_key=" + g.APIKey

	for k, v := range query {
		_url = _url + "&" + k + "=" + url.QueryEscape(v)
	}

	resp, err := http.Get(_url)

	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return GeocodeResult{}, err
	}

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

	if err != nil {
		return GeocodeResult{}, err
	}

	result := GeocodeResult{}

	result.Debug.RequestedURL = _url
	result.Debug.Status = resp.Status
	result.Debug.StatusCode = resp.StatusCode
	result.Debug.RawResponse = body

	err = json.Unmarshal(body, &result)
	if err != nil {
		return result, err
	}

	return result, err
}
<commit_msg>add timeout for connection fails<commit_after>package geocodio

import (
	"encoding/json"
	"errors"
	// "fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// Call uses basic (GET) method to make a request to the API
func (g *Geocodio) Call(path string, query map[string]string) (GeocodeResult, error) {

	if strings.Index(path, "/") != 0 {
		return GeocodeResult{}, errors.New("Path must start with a forward slash: ' / ' ")
	}

	_url := GeocodioAPIBaseURLv1 + path + "?api_key=" + g.APIKey

	for k, v := range query {
		_url = _url + "&" + k + "=" + url.QueryEscape(v)
	}

	// fmt.Println("_url", _url)

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

	resp, err := client.Get(_url)

	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return GeocodeResult{}, err
	}

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

	if err != nil {
		return GeocodeResult{}, err
	}

	result := GeocodeResult{}

	result.Debug.RequestedURL = _url
	result.Debug.Status = resp.Status
	result.Debug.StatusCode = resp.StatusCode
	result.Debug.RawResponse = body

	err = json.Unmarshal(body, &result)
	if err != nil {
		return result, err
	}

	return result, err
}
<|endoftext|>


<commit_before>package whiplash

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"strconv"
	"syscall"
)

const (
	DEBUG = iota
	ERROR
)

// AppSetup switches logging to a file (based on application name) and
// does pidfile handling.
func AppSetup(appname, appver, asockver string) (chan os.Signal) {
	// First, see if an existing pidfile can be read
	pidfile := "/var/run/" + appname + ".pid"
	pb, err := ioutil.ReadFile(pidfile)
	if err == nil {
		// the only safe thing to do, given an extant pidfile, is
		// refuse to start.
		log.Fatalf("pidfile found at %s; pid %s; refusing to start",
			pidfile, string(pb[:len(pb) - 1]))
	}

	// No pidfile, or stale pidfile. Carry on: open logfile in append
	// mode, or create if it doesn't exist
	f, err := os.OpenFile("/var/log/" + appname + ".log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatal(err)
	}
	// set logging to the logfile
	log.SetOutput(f)
	// write startup messages
	log.Printf("====================================== %s %s beginning operations\n", appname, appver)
	log.Printf("asock/client v%s; whiplash lib v%s\n", asockver, Version)

	// write new pidfile
	pidstr := strconv.Itoa(os.Getpid()) + "\n"
	err = ioutil.WriteFile(pidfile, []byte(pidstr), 0644)
	if err != nil {
		log.Fatal(err)
	}
	// and register SIGINT/SIGTERM handler
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)
	return sigchan
}

// AppCleanup deletes an app's pidfile
func AppCleanup(appname string) {
	pidfile := "/var/run/" + appname + ".pid"
	err := os.Remove(pidfile)
	if err != nil {
		log.Printf("couldn't remove %s: %s", pidfile, err)
	}
}
<commit_msg>removing pre-emptive work for internal log level work. not pursuing that now.<commit_after>package whiplash

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"strconv"
	"syscall"
)

// AppSetup switches logging to a file (based on application name) and
// does pidfile handling.
func AppSetup(appname, appver, asockver string) (chan os.Signal) {
	// First, see if an existing pidfile can be read
	pidfile := "/var/run/" + appname + ".pid"
	pb, err := ioutil.ReadFile(pidfile)
	if err == nil {
		// the only safe thing to do, given an extant pidfile, is
		// refuse to start.
		log.Fatalf("pidfile found at %s; pid %s; refusing to start",
			pidfile, string(pb[:len(pb) - 1]))
	}

	// No pidfile, or stale pidfile. Carry on: open logfile in append
	// mode, or create if it doesn't exist
	f, err := os.OpenFile("/var/log/" + appname + ".log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatal(err)
	}
	// set logging to the logfile
	log.SetOutput(f)
	// write startup messages
	log.Printf("====================================== %s %s beginning operations\n", appname, appver)
	log.Printf("asock/client v%s; whiplash lib v%s\n", asockver, Version)

	// write new pidfile
	pidstr := strconv.Itoa(os.Getpid()) + "\n"
	err = ioutil.WriteFile(pidfile, []byte(pidstr), 0644)
	if err != nil {
		log.Fatal(err)
	}
	// and register SIGINT/SIGTERM handler
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)
	return sigchan
}

// AppCleanup deletes an app's pidfile
func AppCleanup(appname string) {
	pidfile := "/var/run/" + appname + ".pid"
	err := os.Remove(pidfile)
	if err != nil {
		log.Printf("couldn't remove %s: %s", pidfile, err)
	}
}
<|endoftext|>


<commit_before><commit_msg>Report errors from CommitOffsets<commit_after><|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type cmdItem struct {
	prefix string
	value  string
}

type app struct {
	ui            *ui
	nav           *nav
	ticker        *time.Ticker
	quitChan      chan bool
	cmd           *exec.Cmd
	cmdIn         io.WriteCloser
	cmdOutBuf     []byte
	cmdHistory    []cmdItem
	cmdHistoryBeg int
	cmdHistoryInd int
}

func newApp() *app {
	ui := newUI()
	nav := newNav(ui.wins[0].h)

	return &app{
		ui:       ui,
		nav:      nav,
		ticker:   new(time.Ticker),
		quitChan: make(chan bool, 1),
	}
}

func (app *app) readFile(path string) {
	log.Printf("reading file: %s", path)

	f, err := os.Open(path)
	if err != nil {
		app.ui.echoerrf("opening file: %s", err)
		return
	}
	defer f.Close()

	p := newParser(f)

	for p.parse() {
		p.expr.eval(app, nil)
	}

	if p.err != nil {
		app.ui.echoerrf("%s", p.err)
	}
}

func (app *app) readHistory() error {
	f, err := os.Open(gHistoryPath)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("opening history file: %s", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		toks := strings.SplitN(scanner.Text(), " ", 2)
		app.cmdHistory = append(app.cmdHistory, cmdItem{toks[0], toks[1]})
	}

	app.cmdHistoryBeg = len(app.cmdHistory)

	if err := scanner.Err(); err != nil {
		return fmt.Errorf("reading history file: %s", err)
	}

	return nil
}

func (app *app) writeHistory() error {
	if len(app.cmdHistory) == 0 {
		return nil
	}

	local := make([]cmdItem, len(app.cmdHistory)-app.cmdHistoryBeg)
	copy(local, app.cmdHistory[app.cmdHistoryBeg:])
	app.cmdHistory = nil

	if err := app.readHistory(); err != nil {
		return fmt.Errorf("reading history file: %s", err)
	}

	app.cmdHistory = append(app.cmdHistory, local...)

	if err := os.MkdirAll(filepath.Dir(gHistoryPath), os.ModePerm); err != nil {
		return fmt.Errorf("creating data directory: %s", err)
	}

	f, err := os.Create(gHistoryPath)
	if err != nil {
		return fmt.Errorf("creating history file: %s", err)
	}
	defer f.Close()

	if len(app.cmdHistory) > 1000 {
		app.cmdHistory = app.cmdHistory[len(app.cmdHistory)-1000:]
	}

	for _, cmd := range app.cmdHistory {
		_, err = f.WriteString(fmt.Sprintf("%s %s\n", cmd.prefix, cmd.value))
		if err != nil {
			return fmt.Errorf("writing history file: %s", err)
		}
	}

	return nil
}

// This is the main event loop of the application. Expressions are read from
// the client and the server on separate goroutines and sent here over channels
// for evaluation. Similarly directories and regular files are also read in
// separate goroutines and sent here for update.
func (app *app) loop() {
	clientChan := app.ui.readExpr()
	serverChan := readExpr()

	if gSelect != "" {
		go func() {
			app.ui.exprChan <- &callExpr{"select", []string{gSelect}, 1}
		}()
	}

	for _, path := range gConfigPaths {
		if _, err := os.Stat(path); !os.IsNotExist(err) {
			app.readFile(path)
		}
	}

	if gCommand != "" {
		p := newParser(strings.NewReader(gCommand))

		for p.parse() {
			p.expr.eval(app, nil)
		}

		if p.err != nil {
			app.ui.echoerrf("%s", p.err)
		}
	}

	for {
		select {
		case <-app.quitChan:
			if app.nav.copyTotal > 0 {
				app.ui.echoerr("quit: copy operation in progress")
				continue
			}

			if app.nav.moveTotal > 0 {
				app.ui.echoerr("quit: move operation in progress")
				continue
			}

			log.Print("bye!")

			if err := app.nav.writeMarks(); err != nil {
				log.Printf("writing marks file: %s", err)
			}

			if err := app.writeHistory(); err != nil {
				log.Printf("writing history file: %s", err)
			}

			if gLastDirPath != "" {
				f, err := os.Create(gLastDirPath)
				if err != nil {
					log.Printf("opening last dir file: %s", err)
				}
				defer f.Close()

				dir := app.nav.currDir()

				_, err = f.WriteString(dir.path)
				if err != nil {
					log.Printf("writing last dir file: %s", err)
				}
			}

			return
		case n := <-app.nav.copyBytesChan:
			app.nav.copyBytes += n
			// n is usually 4096B so update roughly per 4096B x 1024 = 4MB copied
			if app.nav.copyUpdate++; app.nav.copyUpdate >= 1024 {
				app.nav.copyUpdate = 0
				app.ui.draw(app.nav)
			}
		case n := <-app.nav.copyTotalChan:
			app.nav.copyTotal += n
			if n < 0 {
				app.nav.copyBytes += n
			}
			if app.nav.copyTotal == 0 {
				app.nav.copyUpdate = 0
			}
			app.ui.draw(app.nav)
		case n := <-app.nav.moveCountChan:
			app.nav.moveCount += n
			if app.nav.moveUpdate++; app.nav.moveUpdate >= 1000 {
				app.nav.moveUpdate = 0
				app.ui.draw(app.nav)
			}
		case n := <-app.nav.moveTotalChan:
			app.nav.moveTotal += n
			if n < 0 {
				app.nav.moveCount += n
			}
			if app.nav.moveTotal == 0 {
				app.nav.moveUpdate = 0
			}
			app.ui.draw(app.nav)
		case d := <-app.nav.dirChan:
			prev, ok := app.nav.dirCache[d.path]
			if ok {
				d.ind = prev.ind
				d.sel(prev.name(), app.nav.height)
			}

			app.nav.dirCache[d.path] = d

			for i := range app.nav.dirs {
				if app.nav.dirs[i].path == d.path {
					app.nav.dirs[i] = d
				}
			}

			app.nav.position()

			curr, err := app.nav.currFile()
			if err == nil {
				if d.path == app.nav.currDir().path {
					app.ui.loadFile(app.nav)
				}
				if d.path == curr.path {
					app.ui.dirPrev = d
				}
			}

			app.ui.draw(app.nav)
		case r := <-app.nav.regChan:
			app.nav.regCache[r.path] = r

			curr, err := app.nav.currFile()
			if err == nil {
				if r.path == curr.path {
					app.ui.regPrev = r
				}
			}

			app.ui.draw(app.nav)
		case e := <-clientChan:
			e.eval(app, nil)
			app.ui.draw(app.nav)
		case e := <-serverChan:
			e.eval(app, nil)
			app.ui.draw(app.nav)
		case <-app.ticker.C:
			app.nav.renew()
			app.ui.loadFile(app.nav)
			app.ui.draw(app.nav)
		}
	}
}

func (app *app) exportFiles() {
	var currFile string
	if f, err := app.nav.currFile(); err == nil {
		currFile = f.path
	}

	currSelections := app.nav.currSelections()

	exportFiles(currFile, currSelections)
}

func waitKey() error {
	cmd := pauseCommand()

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("waiting key: %s", err)
	}

	return nil
}

// This function is used to run a shell command. Modes are as follows:
//
//     Prefix  Wait  Async  Stdin  Stdout  Stderr  UI action
//     $       No    No     Yes    Yes     Yes     Pause and then resume
//     %       No    No     Yes    Yes     Yes     Statline for input/output
//     !       Yes   No     Yes    Yes     Yes     Pause and then resume
//     &       No    Yes    No     No      No      Do nothing
func (app *app) runShell(s string, args []string, prefix string) {
	app.exportFiles()

	cmd := shellCommand(s, args)

	var out io.Reader
	switch prefix {
	case "$", "!":
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		app.ui.pause()
		defer app.ui.resume()
		defer app.nav.renew()
	case "%":
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Printf("writing stdin: %s", err)
		}
		app.cmdIn = stdin
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Printf("reading stdout: %s", err)
		}
		out = stdout
		cmd.Stderr = cmd.Stdout
	}

	var err error
	switch prefix {
	case "$", "!":
		err = cmd.Run()
	case "%", "&":
		err = cmd.Start()
	}

	if err != nil {
		app.ui.echoerrf("running shell: %s", err)
	}

	switch prefix {
	case "!":
		if err := waitKey(); err != nil {
			app.ui.echoerrf("waiting key: %s", err)
		}
	}

	app.ui.loadFile(app.nav)
	app.ui.loadFileInfo(app.nav)

	switch prefix {
	case "%":
		app.cmd = cmd
		app.cmdOutBuf = nil
		app.ui.msg = ""
		app.ui.cmdPrefix = ">"

		go func() {
			reader := bufio.NewReader(out)
			for {
				b, err := reader.ReadByte()
				if err == io.EOF {
					break
				}
				app.cmdOutBuf = append(app.cmdOutBuf, b)
				app.ui.exprChan <- &callExpr{"echo", []string{string(app.cmdOutBuf)}, 1}
				if b == '\n' || b == '\r' {
					app.cmdOutBuf = nil
				}
			}

			if err := cmd.Wait(); err != nil {
				log.Printf("running shell: %s", err)
			}
			app.cmd = nil
			app.ui.cmdPrefix = ""
			app.ui.exprChan <- &callExpr{"load", nil, 1}
		}()
	}
}
<commit_msg>cd if first argument is a directory<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type cmdItem struct {
	prefix string
	value  string
}

type app struct {
	ui            *ui
	nav           *nav
	ticker        *time.Ticker
	quitChan      chan bool
	cmd           *exec.Cmd
	cmdIn         io.WriteCloser
	cmdOutBuf     []byte
	cmdHistory    []cmdItem
	cmdHistoryBeg int
	cmdHistoryInd int
}

func newApp() *app {
	ui := newUI()
	nav := newNav(ui.wins[0].h)

	return &app{
		ui:       ui,
		nav:      nav,
		ticker:   new(time.Ticker),
		quitChan: make(chan bool, 1),
	}
}

func (app *app) readFile(path string) {
	log.Printf("reading file: %s", path)

	f, err := os.Open(path)
	if err != nil {
		app.ui.echoerrf("opening file: %s", err)
		return
	}
	defer f.Close()

	p := newParser(f)

	for p.parse() {
		p.expr.eval(app, nil)
	}

	if p.err != nil {
		app.ui.echoerrf("%s", p.err)
	}
}

func (app *app) readHistory() error {
	f, err := os.Open(gHistoryPath)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("opening history file: %s", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		toks := strings.SplitN(scanner.Text(), " ", 2)
		app.cmdHistory = append(app.cmdHistory, cmdItem{toks[0], toks[1]})
	}

	app.cmdHistoryBeg = len(app.cmdHistory)

	if err := scanner.Err(); err != nil {
		return fmt.Errorf("reading history file: %s", err)
	}

	return nil
}

func (app *app) writeHistory() error {
	if len(app.cmdHistory) == 0 {
		return nil
	}

	local := make([]cmdItem, len(app.cmdHistory)-app.cmdHistoryBeg)
	copy(local, app.cmdHistory[app.cmdHistoryBeg:])
	app.cmdHistory = nil

	if err := app.readHistory(); err != nil {
		return fmt.Errorf("reading history file: %s", err)
	}

	app.cmdHistory = append(app.cmdHistory, local...)

	if err := os.MkdirAll(filepath.Dir(gHistoryPath), os.ModePerm); err != nil {
		return fmt.Errorf("creating data directory: %s", err)
	}

	f, err := os.Create(gHistoryPath)
	if err != nil {
		return fmt.Errorf("creating history file: %s", err)
	}
	defer f.Close()

	if len(app.cmdHistory) > 1000 {
		app.cmdHistory = app.cmdHistory[len(app.cmdHistory)-1000:]
	}

	for _, cmd := range app.cmdHistory {
		_, err = f.WriteString(fmt.Sprintf("%s %s\n", cmd.prefix, cmd.value))
		if err != nil {
			return fmt.Errorf("writing history file: %s", err)
		}
	}

	return nil
}

// This is the main event loop of the application. Expressions are read from
// the client and the server on separate goroutines and sent here over channels
// for evaluation. Similarly directories and regular files are also read in
// separate goroutines and sent here for update.
func (app *app) loop() {
	clientChan := app.ui.readExpr()
	serverChan := readExpr()

	if gSelect != "" {
		go func() {
			stat, err := os.Stat(gSelect)
			if err != nil {
				app.ui.exprChan <- &callExpr{"echoerr", []string{err.Error()}, 1}
			} else if stat.IsDir() {
				app.ui.exprChan <- &callExpr{"cd", []string{gSelect}, 1}
			} else {
				app.ui.exprChan <- &callExpr{"select", []string{gSelect}, 1}
			}
		}()
	}

	for _, path := range gConfigPaths {
		if _, err := os.Stat(path); !os.IsNotExist(err) {
			app.readFile(path)
		}
	}

	if gCommand != "" {
		p := newParser(strings.NewReader(gCommand))

		for p.parse() {
			p.expr.eval(app, nil)
		}

		if p.err != nil {
			app.ui.echoerrf("%s", p.err)
		}
	}

	for {
		select {
		case <-app.quitChan:
			if app.nav.copyTotal > 0 {
				app.ui.echoerr("quit: copy operation in progress")
				continue
			}

			if app.nav.moveTotal > 0 {
				app.ui.echoerr("quit: move operation in progress")
				continue
			}

			log.Print("bye!")

			if err := app.nav.writeMarks(); err != nil {
				log.Printf("writing marks file: %s", err)
			}

			if err := app.writeHistory(); err != nil {
				log.Printf("writing history file: %s", err)
			}

			if gLastDirPath != "" {
				f, err := os.Create(gLastDirPath)
				if err != nil {
					log.Printf("opening last dir file: %s", err)
				}
				defer f.Close()

				dir := app.nav.currDir()

				_, err = f.WriteString(dir.path)
				if err != nil {
					log.Printf("writing last dir file: %s", err)
				}
			}

			return
		case n := <-app.nav.copyBytesChan:
			app.nav.copyBytes += n
			// n is usually 4096B so update roughly per 4096B x 1024 = 4MB copied
			if app.nav.copyUpdate++; app.nav.copyUpdate >= 1024 {
				app.nav.copyUpdate = 0
				app.ui.draw(app.nav)
			}
		case n := <-app.nav.copyTotalChan:
			app.nav.copyTotal += n
			if n < 0 {
				app.nav.copyBytes += n
			}
			if app.nav.copyTotal == 0 {
				app.nav.copyUpdate = 0
			}
			app.ui.draw(app.nav)
		case n := <-app.nav.moveCountChan:
			app.nav.moveCount += n
			if app.nav.moveUpdate++; app.nav.moveUpdate >= 1000 {
				app.nav.moveUpdate = 0
				app.ui.draw(app.nav)
			}
		case n := <-app.nav.moveTotalChan:
			app.nav.moveTotal += n
			if n < 0 {
				app.nav.moveCount += n
			}
			if app.nav.moveTotal == 0 {
				app.nav.moveUpdate = 0
			}
			app.ui.draw(app.nav)
		case d := <-app.nav.dirChan:
			prev, ok := app.nav.dirCache[d.path]
			if ok {
				d.ind = prev.ind
				d.sel(prev.name(), app.nav.height)
			}

			app.nav.dirCache[d.path] = d

			for i := range app.nav.dirs {
				if app.nav.dirs[i].path == d.path {
					app.nav.dirs[i] = d
				}
			}

			app.nav.position()

			curr, err := app.nav.currFile()
			if err == nil {
				if d.path == app.nav.currDir().path {
					app.ui.loadFile(app.nav)
				}
				if d.path == curr.path {
					app.ui.dirPrev = d
				}
			}

			app.ui.draw(app.nav)
		case r := <-app.nav.regChan:
			app.nav.regCache[r.path] = r

			curr, err := app.nav.currFile()
			if err == nil {
				if r.path == curr.path {
					app.ui.regPrev = r
				}
			}

			app.ui.draw(app.nav)
		case e := <-clientChan:
			e.eval(app, nil)
			app.ui.draw(app.nav)
		case e := <-serverChan:
			e.eval(app, nil)
			app.ui.draw(app.nav)
		case <-app.ticker.C:
			app.nav.renew()
			app.ui.loadFile(app.nav)
			app.ui.draw(app.nav)
		}
	}
}

func (app *app) exportFiles() {
	var currFile string
	if f, err := app.nav.currFile(); err == nil {
		currFile = f.path
	}

	currSelections := app.nav.currSelections()

	exportFiles(currFile, currSelections)
}

func waitKey() error {
	cmd := pauseCommand()

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("waiting key: %s", err)
	}

	return nil
}

// This function is used to run a shell command. Modes are as follows:
//
//     Prefix  Wait  Async  Stdin  Stdout  Stderr  UI action
//     $       No    No     Yes    Yes     Yes     Pause and then resume
//     %       No    No     Yes    Yes     Yes     Statline for input/output
//     !       Yes   No     Yes    Yes     Yes     Pause and then resume
//     &       No    Yes    No     No      No      Do nothing
func (app *app) runShell(s string, args []string, prefix string) {
	app.exportFiles()

	cmd := shellCommand(s, args)

	var out io.Reader
	switch prefix {
	case "$", "!":
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		app.ui.pause()
		defer app.ui.resume()
		defer app.nav.renew()
	case "%":
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Printf("writing stdin: %s", err)
		}
		app.cmdIn = stdin
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Printf("reading stdout: %s", err)
		}
		out = stdout
		cmd.Stderr = cmd.Stdout
	}

	var err error
	switch prefix {
	case "$", "!":
		err = cmd.Run()
	case "%", "&":
		err = cmd.Start()
	}

	if err != nil {
		app.ui.echoerrf("running shell: %s", err)
	}

	switch prefix {
	case "!":
		if err := waitKey(); err != nil {
			app.ui.echoerrf("waiting key: %s", err)
		}
	}

	app.ui.loadFile(app.nav)
	app.ui.loadFileInfo(app.nav)

	switch prefix {
	case "%":
		app.cmd = cmd
		app.cmdOutBuf = nil
		app.ui.msg = ""
		app.ui.cmdPrefix = ">"

		go func() {
			reader := bufio.NewReader(out)
			for {
				b, err := reader.ReadByte()
				if err == io.EOF {
					break
				}
				app.cmdOutBuf = append(app.cmdOutBuf, b)
				app.ui.exprChan <- &callExpr{"echo", []string{string(app.cmdOutBuf)}, 1}
				if b == '\n' || b == '\r' {
					app.cmdOutBuf = nil
				}
			}

			if err := cmd.Wait(); err != nil {
				log.Printf("running shell: %s", err)
			}
			app.cmd = nil
			app.ui.cmdPrefix = ""
			app.ui.exprChan <- &callExpr{"load", nil, 1}
		}()
	}
}
<|endoftext|>


<commit_before>package gallium

/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo CFLAGS: -DGALLIUM_DIR=${SRCDIR}
#cgo CFLAGS: -Idist/include
#cgo LDFLAGS: -F${SRCDIR}/dist
#cgo LDFLAGS: -framework Gallium
#cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/dist
#cgo LDFLAGS: -mmacosx-version-min=10.8

#include <stdlib.h>
#include "gallium/gallium.h"
#include "gallium/cocoa.h"

// It does not seem that we can import "_cgo_export.h" from here
extern void cgo_onReady(int);

// This is a wrapper around GalliumLoop that adds the function pointer
// argument, since this does not seem to be possible from Go directly.
static inline void helper_GalliumLoop(int app_id, const char* arg0, struct gallium_error** err) {
	GalliumLoop(app_id, arg0, &cgo_onReady, err);
}
*/
import "C"
import (
	"errors"
	"fmt"
	"log"
	"time"
	"unsafe"
)

// cerr holds a C-allocated error, which must be freed explicitly.
type cerr struct {
	c *C.struct_gallium_error
}

// newCerr allocates a new error struct. It must be freed explicitly.
func newCerr() cerr {
	return cerr{
		c: (*C.struct_gallium_error)(C.malloc(C.sizeof_struct_gallium_error)),
	}
}

func (e cerr) free() {
	C.free(unsafe.Pointer(e.c))
}

func (e *cerr) err() error {
	// TODO
	return fmt.Errorf("C error")
}

// Loop starts the browser loop and does not return unless there is an initialization error
func Loop(args []string, onReady func(*App)) error {
	log.Println("\n\n=== gallium.Loop ===")
	cerr := newCerr()
	defer cerr.free()

	app := App{
		ready: make(chan struct{}),
	}

	go func() {
		select {
		case <-app.ready:
			onReady(&app)
		case <-time.After(3 * time.Second):
			log.Fatal("Waited for 3 seconds without ready signal")
		}
	}()

	appId := apps.add(&app)
	C.helper_GalliumLoop(C.int(appId), C.CString(args[0]), &cerr.c)
	return cerr.err()
}

// appManager is the singleton for managing app instances
type appManager []*App

func (m *appManager) add(app *App) int {
	id := len(*m)
	*m = append(*m, app)
	return id
}

func (m *appManager) get(id int) *App {
	return (*m)[id]
}

var apps appManager

// App is the handle that allows you to create windows and menus
type App struct {
	// ready is how the cgo onready callback indicates to the Loop goroutine that
	// chromium is initialized
	ready chan struct{}
}

// Rect represents a rectangular region on the screen
type Rect struct {
	Width  int // Width in pixels
	Height int // Height in pixels
	Left   int // Left is offset from left in pixel
	Top    int // Left is offset from top in pixels
}

// WindowOptions contains options for creating windows
type WindowOptions struct {
	Title            string // String to display in title bar
	Shape            Rect   // Initial size and position of window
	TitleBar         bool   // Whether the window title bar
	Frame            bool   // Whether the window has a frame
	Resizable        bool   // Whether the window border can be dragged to change its shape
	CloseButton      bool   // Whether the window has a close button
	MinButton        bool   // Whether the window has a miniaturize button
	FullScreenButton bool   // Whether the window has a full screen button
	Menu             []MenuEntry
}

// FramedWindow contains options for an "ordinary" window with title bar,
// frame, and min/max/close buttons.
var FramedWindow = WindowOptions{
	Shape: Rect{
		Width:  800,
		Height: 600,
		Left:   100,
		Top:    100,
	},
	TitleBar:         true,
	Frame:            true,
	Resizable:        true,
	CloseButton:      true,
	MinButton:        true,
	FullScreenButton: true,
	Title:            "Gallium",
}

// FramelessWindow contains options for a window with no frame or border, but that
// is still resizable.
var FramelessWindow = WindowOptions{
	Shape: Rect{
		Width:  800,
		Height: 600,
		Left:   100,
		Top:    100,
	},
	Resizable: true,
}

// Window represents a window registered with the native UI toolkit (e.g. NSWindow on macOS)
type Window struct {
	c *C.gallium_window_t
}

var (
	errZeroWidth  = errors.New("window width was zero")
	errZeroHeight = errors.New("window height was zero")
)

// OpenWindow creates a window that will load the given URL.
func (b *App) OpenWindow(url string, opt WindowOptions) (*Window, error) {
	if opt.Shape.Width == 0 {
		return nil, errZeroWidth
	}
	if opt.Shape.Height == 0 {
		return nil, errZeroHeight
	}
	// Create the Cocoa window
	cwin := C.GalliumOpenWindow(
		C.CString(url),
		C.CString(opt.Title),
		C.int(opt.Shape.Width),
		C.int(opt.Shape.Height),
		C.int(opt.Shape.Left),
		C.int(opt.Shape.Top),
		C.bool(opt.TitleBar),
		C.bool(opt.Frame),
		C.bool(opt.Resizable),
		C.bool(opt.CloseButton),
		C.bool(opt.MinButton),
		C.bool(opt.FullScreenButton))

	// TODO: associate menu
	return &Window{
		c: cwin,
	}, nil
}

// Shape gets the current shape of the window.
func (w *Window) Shape() Rect {
	return Rect{
		Width:  int(C.GalliumWindowGetWidth(w.c)),
		Height: int(C.GalliumWindowGetHeight(w.c)),
		Left:   int(C.GalliumWindowGetLeft(w.c)),
		Top:    int(C.GalliumWindowGetTop(w.c)),
	}
}

// Shape gets the current shape of the window.
func (w *Window) SetShape(r Rect) {
	C.GalliumWindowSetShape(w.c, C.int(r.Width), C.int(r.Height), C.int(r.Left), C.int(r.Top))
}

// URL gets the URL that the window is currently at.
func (w *Window) URL() string {
	return C.GoString(C.GalliumWindowGetURL(w.c))
}

// LoadURL causes the window to load the given URL
func (w *Window) LoadURL(url string) {
	log.Println("Window.LoadURL:", url)
	C.GalliumWindowLoadURL(w.c, C.CString(url))
}

// OpenDevTools shows the developer tools, or does nothing if they are already open.
func (w *Window) OpenDevTools() {
	C.GalliumWindowOpenDevTools(w.c)
}

// CloseDevTools closes the developer tools, or does nothing if they are already closed.
func (w *Window) CloseDevTools() {
	C.GalliumWindowCloseDevTools(w.c)
}

// DevToolsVisible returns whether the developer tools are showing
func (w *Window) DevToolsAreOpen() bool {
	return bool(C.GalliumWindowDevToolsAreOpen(w.c))
}
<commit_msg>fix OpenWindow receiver<commit_after>package gallium

/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo CFLAGS: -DGALLIUM_DIR=${SRCDIR}
#cgo CFLAGS: -Idist/include
#cgo LDFLAGS: -F${SRCDIR}/dist
#cgo LDFLAGS: -framework Gallium
#cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/dist
#cgo LDFLAGS: -mmacosx-version-min=10.8

#include <stdlib.h>
#include "gallium/gallium.h"
#include "gallium/cocoa.h"

// It does not seem that we can import "_cgo_export.h" from here
extern void cgo_onReady(int);

// This is a wrapper around GalliumLoop that adds the function pointer
// argument, since this does not seem to be possible from Go directly.
static inline void helper_GalliumLoop(int app_id, const char* arg0, struct gallium_error** err) {
	GalliumLoop(app_id, arg0, &cgo_onReady, err);
}
*/
import "C"
import (
	"errors"
	"fmt"
	"log"
	"time"
	"unsafe"
)

// cerr holds a C-allocated error, which must be freed explicitly.
type cerr struct {
	c *C.struct_gallium_error
}

// newCerr allocates a new error struct. It must be freed explicitly.
func newCerr() cerr {
	return cerr{
		c: (*C.struct_gallium_error)(C.malloc(C.sizeof_struct_gallium_error)),
	}
}

func (e cerr) free() {
	C.free(unsafe.Pointer(e.c))
}

func (e *cerr) err() error {
	// TODO
	return fmt.Errorf("C error")
}

// Loop starts the browser loop and does not return unless there is an initialization error
func Loop(args []string, onReady func(*App)) error {
	log.Println("\n\n=== gallium.Loop ===")
	cerr := newCerr()
	defer cerr.free()

	app := App{
		ready: make(chan struct{}),
	}

	go func() {
		select {
		case <-app.ready:
			onReady(&app)
		case <-time.After(3 * time.Second):
			log.Fatal("Waited for 3 seconds without ready signal")
		}
	}()

	appId := apps.add(&app)
	C.helper_GalliumLoop(C.int(appId), C.CString(args[0]), &cerr.c)
	return cerr.err()
}

// appManager is the singleton for managing app instances
type appManager []*App

func (m *appManager) add(app *App) int {
	id := len(*m)
	*m = append(*m, app)
	return id
}

func (m *appManager) get(id int) *App {
	return (*m)[id]
}

var apps appManager

// App is the handle that allows you to create windows and menus
type App struct {
	// ready is how the cgo onready callback indicates to the Loop goroutine that
	// chromium is initialized
	ready chan struct{}
}

// Rect represents a rectangular region on the screen
type Rect struct {
	Width  int // Width in pixels
	Height int // Height in pixels
	Left   int // Left is offset from left in pixel
	Top    int // Left is offset from top in pixels
}

// WindowOptions contains options for creating windows
type WindowOptions struct {
	Title            string // String to display in title bar
	Shape            Rect   // Initial size and position of window
	TitleBar         bool   // Whether the window title bar
	Frame            bool   // Whether the window has a frame
	Resizable        bool   // Whether the window border can be dragged to change its shape
	CloseButton      bool   // Whether the window has a close button
	MinButton        bool   // Whether the window has a miniaturize button
	FullScreenButton bool   // Whether the window has a full screen button
	Menu             []MenuEntry
}

// FramedWindow contains options for an "ordinary" window with title bar,
// frame, and min/max/close buttons.
var FramedWindow = WindowOptions{
	Shape: Rect{
		Width:  800,
		Height: 600,
		Left:   100,
		Top:    100,
	},
	TitleBar:         true,
	Frame:            true,
	Resizable:        true,
	CloseButton:      true,
	MinButton:        true,
	FullScreenButton: true,
	Title:            "Gallium",
}

// FramelessWindow contains options for a window with no frame or border, but that
// is still resizable.
var FramelessWindow = WindowOptions{
	Shape: Rect{
		Width:  800,
		Height: 600,
		Left:   100,
		Top:    100,
	},
	Resizable: true,
}

// Window represents a window registered with the native UI toolkit (e.g. NSWindow on macOS)
type Window struct {
	c *C.gallium_window_t
}

var (
	errZeroWidth  = errors.New("window width was zero")
	errZeroHeight = errors.New("window height was zero")
)

// OpenWindow creates a window that will load the given URL.
func (app *App) OpenWindow(url string, opt WindowOptions) (*Window, error) {
	if opt.Shape.Width == 0 {
		return nil, errZeroWidth
	}
	if opt.Shape.Height == 0 {
		return nil, errZeroHeight
	}
	// Create the Cocoa window
	cwin := C.GalliumOpenWindow(
		C.CString(url),
		C.CString(opt.Title),
		C.int(opt.Shape.Width),
		C.int(opt.Shape.Height),
		C.int(opt.Shape.Left),
		C.int(opt.Shape.Top),
		C.bool(opt.TitleBar),
		C.bool(opt.Frame),
		C.bool(opt.Resizable),
		C.bool(opt.CloseButton),
		C.bool(opt.MinButton),
		C.bool(opt.FullScreenButton))

	// TODO: associate menu
	return &Window{
		c: cwin,
	}, nil
}

// Shape gets the current shape of the window.
func (w *Window) Shape() Rect {
	return Rect{
		Width:  int(C.GalliumWindowGetWidth(w.c)),
		Height: int(C.GalliumWindowGetHeight(w.c)),
		Left:   int(C.GalliumWindowGetLeft(w.c)),
		Top:    int(C.GalliumWindowGetTop(w.c)),
	}
}

// Shape gets the current shape of the window.
func (w *Window) SetShape(r Rect) {
	C.GalliumWindowSetShape(w.c, C.int(r.Width), C.int(r.Height), C.int(r.Left), C.int(r.Top))
}

// URL gets the URL that the window is currently at.
func (w *Window) URL() string {
	return C.GoString(C.GalliumWindowGetURL(w.c))
}

// LoadURL causes the window to load the given URL
func (w *Window) LoadURL(url string) {
	log.Println("Window.LoadURL:", url)
	C.GalliumWindowLoadURL(w.c, C.CString(url))
}

// OpenDevTools shows the developer tools, or does nothing if they are already open.
func (w *Window) OpenDevTools() {
	C.GalliumWindowOpenDevTools(w.c)
}

// CloseDevTools closes the developer tools, or does nothing if they are already closed.
func (w *Window) CloseDevTools() {
	C.GalliumWindowCloseDevTools(w.c)
}

// DevToolsVisible returns whether the developer tools are showing
func (w *Window) DevToolsAreOpen() bool {
	return bool(C.GalliumWindowDevToolsAreOpen(w.c))
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package lifecycle

import (
	"fmt"
	"strings"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/common"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const resizeNodeReadyTimeout = 2 * time.Minute

func resizeRC(c clientset.Interface, ns, name string, replicas int32) error {
	rc, err := c.CoreV1().ReplicationControllers(ns).Get(name, metav1.GetOptions{})
	if err != nil {
		return err
	}
	*(rc.Spec.Replicas) = replicas
	_, err = c.CoreV1().ReplicationControllers(rc.Namespace).Update(rc)
	return err
}

var _ = SIGDescribe("Nodes [Disruptive]", func() {
	f := framework.NewDefaultFramework("resize-nodes")
	var systemPodsNo int32
	var c clientset.Interface
	var ns string
	ignoreLabels := framework.ImagePullerLabels
	var group string

	BeforeEach(func() {
		c = f.ClientSet
		ns = f.Namespace.Name
		systemPods, err := framework.GetPodsInNamespace(c, ns, ignoreLabels)
		Expect(err).NotTo(HaveOccurred())
		systemPodsNo = int32(len(systemPods))
		if strings.Index(framework.TestContext.CloudConfig.NodeInstanceGroup, ",") >= 0 {
			framework.Failf("Test dose not support cluster setup with more than one MIG: %s", framework.TestContext.CloudConfig.NodeInstanceGroup)
		} else {
			group = framework.TestContext.CloudConfig.NodeInstanceGroup
		}
	})

	// Slow issue #13323 (8 min)
	Describe("Resize [Slow]", func() {
		var skipped bool

		BeforeEach(func() {
			skipped = true
			framework.SkipUnlessProviderIs("gce", "gke", "aws")
			framework.SkipUnlessNodeCountIsAtLeast(2)
			skipped = false
		})

		AfterEach(func() {
			if skipped {
				return
			}

			By("restoring the original node instance group size")
			if err := framework.ResizeGroup(group, int32(framework.TestContext.CloudConfig.NumNodes)); err != nil {
				framework.Failf("Couldn't restore the original node instance group size: %v", err)
			}
			// In GKE, our current tunneling setup has the potential to hold on to a broken tunnel (from a
			// rebooted/deleted node) for up to 5 minutes before all tunnels are dropped and recreated.
			// Most tests make use of some proxy feature to verify functionality. So, if a reboot test runs
			// right before a test that tries to get logs, for example, we may get unlucky and try to use a
			// closed tunnel to a node that was recently rebooted. There's no good way to framework.Poll for proxies
			// being closed, so we sleep.
			//
			// TODO(cjcullen) reduce this sleep (#19314)
			if framework.ProviderIs("gke") {
				By("waiting 5 minutes for all dead tunnels to be dropped")
				time.Sleep(5 * time.Minute)
			}
			if err := framework.WaitForGroupSize(group, int32(framework.TestContext.CloudConfig.NumNodes)); err != nil {
				framework.Failf("Couldn't restore the original node instance group size: %v", err)
			}
			if err := framework.WaitForReadyNodes(c, framework.TestContext.CloudConfig.NumNodes, 10*time.Minute); err != nil {
				framework.Failf("Couldn't restore the original cluster size: %v", err)
			}
			// Many e2e tests assume that the cluster is fully healthy before they start.  Wait until
			// the cluster is restored to health.
			By("waiting for system pods to successfully restart")
			err := framework.WaitForPodsRunningReady(c, metav1.NamespaceSystem, systemPodsNo, 0, framework.PodReadyBeforeTimeout, ignoreLabels)
			Expect(err).NotTo(HaveOccurred())
			By("waiting for image prepulling pods to complete")
			framework.WaitForPodsSuccess(c, metav1.NamespaceSystem, framework.ImagePullerLabels, framework.ImagePrePullingTimeout)
		})

		It("should be able to delete nodes", func() {
			// Create a replication controller for a service that serves its hostname.
			// The source for the Docker container kubernetes/serve_hostname is in contrib/for-demos/serve_hostname
			name := "my-hostname-delete-node"
			numNodes, err := framework.NumberOfRegisteredNodes(c)
			Expect(err).NotTo(HaveOccurred())
			replicas := int32(numNodes)
			common.NewRCByName(c, ns, name, replicas, nil)
			err = framework.VerifyPods(c, ns, name, true, replicas)
			Expect(err).NotTo(HaveOccurred())

			By(fmt.Sprintf("decreasing cluster size to %d", replicas-1))
			err = framework.ResizeGroup(group, replicas-1)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForGroupSize(group, replicas-1)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForReadyNodes(c, int(replicas-1), 10*time.Minute)
			Expect(err).NotTo(HaveOccurred())

			By("waiting 1 minute for the watch in the podGC to catch up, remove any pods scheduled on " +
				"the now non-existent node and the RC to recreate it")
			time.Sleep(time.Minute)

			By("verifying whether the pods from the removed node are recreated")
			err = framework.VerifyPods(c, ns, name, true, replicas)
			Expect(err).NotTo(HaveOccurred())
		})

		// TODO: Bug here - testName is not correct
		It("should be able to add nodes", func() {
			// Create a replication controller for a service that serves its hostname.
			// The source for the Docker container kubernetes/serve_hostname is in contrib/for-demos/serve_hostname
			name := "my-hostname-add-node"
			common.NewSVCByName(c, ns, name)
			numNodes, err := framework.NumberOfRegisteredNodes(c)
			Expect(err).NotTo(HaveOccurred())
			replicas := int32(numNodes)
			common.NewRCByName(c, ns, name, replicas, nil)
			err = framework.VerifyPods(c, ns, name, true, replicas)
			Expect(err).NotTo(HaveOccurred())

			By(fmt.Sprintf("increasing cluster size to %d", replicas+1))
			err = framework.ResizeGroup(group, replicas+1)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForGroupSize(group, replicas+1)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForReadyNodes(c, int(replicas+1), 10*time.Minute)
			Expect(err).NotTo(HaveOccurred())

			By(fmt.Sprintf("increasing size of the replication controller to %d and verifying all pods are running", replicas+1))
			err = resizeRC(c, ns, name, replicas+1)
			Expect(err).NotTo(HaveOccurred())
			err = framework.VerifyPods(c, ns, name, true, replicas+1)
			Expect(err).NotTo(HaveOccurred())
		})
	})
})
<commit_msg>Fix resize nodes tests for Regional Clusters<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package lifecycle

import (
	"fmt"
	"strings"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/common"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const resizeNodeReadyTimeout = 2 * time.Minute

func resizeRC(c clientset.Interface, ns, name string, replicas int32) error {
	rc, err := c.CoreV1().ReplicationControllers(ns).Get(name, metav1.GetOptions{})
	if err != nil {
		return err
	}
	*(rc.Spec.Replicas) = replicas
	_, err = c.CoreV1().ReplicationControllers(rc.Namespace).Update(rc)
	return err
}

var _ = SIGDescribe("Nodes [Disruptive]", func() {
	f := framework.NewDefaultFramework("resize-nodes")
	var systemPodsNo int32
	var c clientset.Interface
	var ns string
	ignoreLabels := framework.ImagePullerLabels
	var group string

	BeforeEach(func() {
		c = f.ClientSet
		ns = f.Namespace.Name
		systemPods, err := framework.GetPodsInNamespace(c, ns, ignoreLabels)
		Expect(err).NotTo(HaveOccurred())
		systemPodsNo = int32(len(systemPods))
		if strings.Index(framework.TestContext.CloudConfig.NodeInstanceGroup, ",") >= 0 {
			framework.Failf("Test dose not support cluster setup with more than one MIG: %s", framework.TestContext.CloudConfig.NodeInstanceGroup)
		} else {
			group = framework.TestContext.CloudConfig.NodeInstanceGroup
		}
	})

	// Slow issue #13323 (8 min)
	Describe("Resize [Slow]", func() {
		var skipped bool

		BeforeEach(func() {
			skipped = true
			framework.SkipUnlessProviderIs("gce", "gke", "aws")
			framework.SkipUnlessNodeCountIsAtLeast(2)
			skipped = false
		})

		AfterEach(func() {
			if skipped {
				return
			}

			By("restoring the original node instance group size")
			if err := framework.ResizeGroup(group, int32(framework.TestContext.CloudConfig.NumNodes)); err != nil {
				framework.Failf("Couldn't restore the original node instance group size: %v", err)
			}
			// In GKE, our current tunneling setup has the potential to hold on to a broken tunnel (from a
			// rebooted/deleted node) for up to 5 minutes before all tunnels are dropped and recreated.
			// Most tests make use of some proxy feature to verify functionality. So, if a reboot test runs
			// right before a test that tries to get logs, for example, we may get unlucky and try to use a
			// closed tunnel to a node that was recently rebooted. There's no good way to framework.Poll for proxies
			// being closed, so we sleep.
			//
			// TODO(cjcullen) reduce this sleep (#19314)
			if framework.ProviderIs("gke") {
				By("waiting 5 minutes for all dead tunnels to be dropped")
				time.Sleep(5 * time.Minute)
			}
			if err := framework.WaitForGroupSize(group, int32(framework.TestContext.CloudConfig.NumNodes)); err != nil {
				framework.Failf("Couldn't restore the original node instance group size: %v", err)
			}
			if err := framework.WaitForReadyNodes(c, framework.TestContext.CloudConfig.NumNodes, 10*time.Minute); err != nil {
				framework.Failf("Couldn't restore the original cluster size: %v", err)
			}
			// Many e2e tests assume that the cluster is fully healthy before they start.  Wait until
			// the cluster is restored to health.
			By("waiting for system pods to successfully restart")
			err := framework.WaitForPodsRunningReady(c, metav1.NamespaceSystem, systemPodsNo, 0, framework.PodReadyBeforeTimeout, ignoreLabels)
			Expect(err).NotTo(HaveOccurred())
			By("waiting for image prepulling pods to complete")
			framework.WaitForPodsSuccess(c, metav1.NamespaceSystem, framework.ImagePullerLabels, framework.ImagePrePullingTimeout)
		})

		It("should be able to delete nodes", func() {
			// Create a replication controller for a service that serves its hostname.
			// The source for the Docker container kubernetes/serve_hostname is in contrib/for-demos/serve_hostname
			name := "my-hostname-delete-node"
			numNodes, err := framework.NumberOfRegisteredNodes(c)
			Expect(err).NotTo(HaveOccurred())
			replicas := int32(numNodes)
			common.NewRCByName(c, ns, name, replicas, nil)
			err = framework.VerifyPods(c, ns, name, true, replicas)
			Expect(err).NotTo(HaveOccurred())

			targetNumNodes := int32(framework.TestContext.CloudConfig.NumNodes - 1)
			By(fmt.Sprintf("decreasing cluster size to %d", targetNumNodes))
			err = framework.ResizeGroup(group, targetNumNodes)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForGroupSize(group, targetNumNodes)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForReadyNodes(c, int(replicas-1), 10*time.Minute)
			Expect(err).NotTo(HaveOccurred())

			By("waiting 1 minute for the watch in the podGC to catch up, remove any pods scheduled on " +
				"the now non-existent node and the RC to recreate it")
			time.Sleep(time.Minute)

			By("verifying whether the pods from the removed node are recreated")
			err = framework.VerifyPods(c, ns, name, true, replicas)
			Expect(err).NotTo(HaveOccurred())
		})

		// TODO: Bug here - testName is not correct
		It("should be able to add nodes", func() {
			// Create a replication controller for a service that serves its hostname.
			// The source for the Docker container kubernetes/serve_hostname is in contrib/for-demos/serve_hostname
			name := "my-hostname-add-node"
			common.NewSVCByName(c, ns, name)
			numNodes, err := framework.NumberOfRegisteredNodes(c)
			Expect(err).NotTo(HaveOccurred())
			replicas := int32(numNodes)
			common.NewRCByName(c, ns, name, replicas, nil)
			err = framework.VerifyPods(c, ns, name, true, replicas)
			Expect(err).NotTo(HaveOccurred())

			targetNumNodes := int32(framework.TestContext.CloudConfig.NumNodes + 1)
			By(fmt.Sprintf("increasing cluster size to %d", targetNumNodes))
			err = framework.ResizeGroup(group, targetNumNodes)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForGroupSize(group, targetNumNodes)
			Expect(err).NotTo(HaveOccurred())
			err = framework.WaitForReadyNodes(c, int(replicas+1), 10*time.Minute)
			Expect(err).NotTo(HaveOccurred())

			By(fmt.Sprintf("increasing size of the replication controller to %d and verifying all pods are running", replicas+1))
			err = resizeRC(c, ns, name, replicas+1)
			Expect(err).NotTo(HaveOccurred())
			err = framework.VerifyPods(c, ns, name, true, replicas+1)
			Expect(err).NotTo(HaveOccurred())
		})
	})
})
<|endoftext|>


<commit_before>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grpc_test

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"testing"
	"time"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/fault"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/net/grpcutil"
	"github.com/google/gapid/test/robot/stash"
	stashgrpc "github.com/google/gapid/test/robot/stash/grpc"
	"github.com/google/gapid/test/robot/stash/local"
	"google.golang.org/grpc"
)

func checkReadFromStash(ctx context.Context, assert assert.Manager, rnd *rand.Rand, s *stash.Client, exp map[string][]byte) {
	for id, expected := range exp {
		e, err := s.Lookup(ctx, id)
		assert.For("lookup").ThatError(err).Succeeded()
		assert.For("lookup").That(e).IsNotNil()

		assert.For("lookup").That(e.Length).Equals(int64(len(expected)))
		rs, err := s.Open(ctx, id)
		assert.For("open").ThatError(err).Succeeded()

		data, err := ioutil.ReadAll(rs)
		assert.For("readall").ThatError(err).Succeeded()
		assert.For("readall").ThatSlice(data).Equals(expected)

		data, err = readInRandomOrder(rnd, rs, 60, 3)
		assert.For("read random").ThatError(err).Succeeded()
		assert.For("read random").ThatSlice(data).Equals(expected)

		data, err = s.Read(ctx, id)
		assert.For("stash.Read").ThatError(err).Succeeded()
		assert.For("stash.Read").ThatSlice(data).Equals(expected)
	}
}

func uploadRandomDataToStash(ctx context.Context, assert assert.Manager, r *rand.Rand, s *stash.Client, lens []int) map[string][]byte {
	m := map[string][]byte{}
	for i, ln := range lens {
		data := randomBytes(r, ln)
		id, err := s.UploadBytes(ctx, stash.Upload{Name: []string{fmt.Sprintf("%d.bin", i)}, Type: []string{"application/binary"}}, data)
		assert.For("upload").ThatError(err).Succeeded()
		m[id] = data
	}
	assert.For("upload finished").ThatInteger(len(m)).Equals(len(lens))
	return m
}

func TestMemoryAndGrpcStash(t *testing.T) {
	assert := assert.Context(t)
	ctx := log.Testing(t)
	r := rand.New(rand.NewSource(12345))

	memStash := local.NewMemoryService()
	testData := uploadRandomDataToStash(ctx, assert, r, memStash, []int{10, 100, 100, 1, 100000, 100000, 0})
	assert.For("test data length").ThatInteger(len(testData)).IsAtLeast(1)
	checkReadFromStash(ctx, assert, r, memStash, testData)

	var srv *grpc.Server
	go grpcutil.ServeWithListener(
		ctx,
		grpcutil.NewPipeListener("pipe:stashgrpc"),
		func(ctx context.Context, listener net.Listener, server *grpc.Server) error {
			srv = server
			return stashgrpc.Serve(ctx, server, memStash)
		},
	)

	ok := fault.Const("ok")
	err := grpcutil.Client(ctx, "pipe:stashgrpc", func(ctx context.Context, cc *grpc.ClientConn) error {
		sc := stashgrpc.MustConnect(ctx, cc)
		// Make sure the in-memory stash is correctly exposed.
		checkReadFromStash(ctx, assert, r, sc, testData)
		// Make sure upload works through GRPC as well.
		testData = uploadRandomDataToStash(ctx, assert, r, sc, []int{17})
		checkReadFromStash(ctx, assert, r, sc, testData)
		return ok
	}, grpc.WithDialer(grpcutil.GetDialer(ctx)), grpc.WithTimeout(1*time.Second), grpc.WithInsecure())
	assert.For("").ThatError(err).Equals(ok)

	srv.GracefulStop()
}

func randomBytes(r *rand.Rand, length int) []byte {
	p := make([]byte, length)
	r.Read(p)
	return p
}

func TestReadInRandomOrder(t *testing.T) {
	assert := assert.Context(t)
	r := rand.New(rand.NewSource(12345))

	for _, c := range []struct {
		length         int
		sections       int
		maxConsecutive int
	}{
		{1000, 40, 5},
		{1000, 1, 1},
		{1000, 1, 30},
		{1000, 40, 1},
		{1, 1, 1},
	} {
		data := randomBytes(r, c.length)
		got, err := readInRandomOrder(r, bytes.NewReader(data), c.sections, c.maxConsecutive)
		assert.For("read random").ThatError(err).Succeeded()
		assert.For("read random").ThatSlice(got).Equals(data)
	}
}

// readInRandom order exercises an io.ReadSeeker by splitting it up in sections which are
// read in random order. Within a section, several consecutive reads may be performed.
// All types of seeking are used.
func readInRandomOrder(r *rand.Rand, rs io.ReadSeeker, sections int, maxConsecutiveReads int) ([]byte, error) {
	length, err := rs.Seek(0, io.SeekEnd)
	if err != nil {
		return nil, err
	}
	var pos int64 = length

	data := make([]byte, length)

	for _, s := range randomSections(r, int(length), sections, maxConsecutiveReads) {
		switch r.Intn(3) {
		case 0:
			pos, err = rs.Seek(s.offset, io.SeekStart)
		case 1:
			pos, err = rs.Seek(s.offset-length, io.SeekEnd)
		case 2:
			pos, err = rs.Seek(s.offset-pos, io.SeekCurrent)
		}
		if err != nil {
			return nil, err
		}
		if pos != s.offset {
			return nil, errors.New("Incorrect offset returned")
		}
		for _, rd := range s.reads {
			n, err := io.ReadAtLeast(rs, data[int(pos):int(pos)+rd], rd)
			if err != nil {
				return nil, err
			}
			if n != rd {
				return nil, fmt.Errorf("Expected %d bytes to be read, got %d", rd, n)
			}
			pos += int64(n)
		}
	}
	return data, nil
}

type readSection struct {
	offset int64 // absolute section offset
	reads  []int // lengths of consecutive reads within section
}

func randomSections(r *rand.Rand, length int, n int, maxReadsPerSection int) []readSection {
	if length == 0 {
		return []readSection{{0, []int{0}}}
	}
	min := func(a, b int) int {
		if a < b {
			return a
		}
		return b
	}
	sections := make([]readSection, n)
	p := r.Perm(n)
	n = min(n, length)
	ch := randomChunks(r, length, n)
	offset := 0
	for i := 0; i < n; i++ {
		sections[p[i]] = readSection{
			offset: int64(offset),
			reads:  randomChunks(r, ch[i], min(ch[i], 1+r.Intn(maxReadsPerSection))),
		}
		offset += ch[i]
	}
	return sections
}

func randomChunks(r *rand.Rand, length int, n int) []int {
	chunks := make([]int, n)

	sum := 0
	for i := 0; i < n; i++ {
		sz := r.Intn(length / n)
		chunks[i] = sz
		sum += sz
	}
	delta := (length - sum) / n
	for i := 0; i < len(chunks); i++ {
		chunks[i] += delta
		sum += delta
	}
	chunks[r.Intn(len(chunks))] += (length - sum)
	return chunks
}
<commit_msg>test/robot/stash/grpc: Make the test deterministic.<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grpc_test

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"testing"
	"time"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/fault"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/net/grpcutil"
	"github.com/google/gapid/test/robot/stash"
	stashgrpc "github.com/google/gapid/test/robot/stash/grpc"
	"github.com/google/gapid/test/robot/stash/local"
	"google.golang.org/grpc"
)

type data struct {
	id    string
	bytes []byte
}

func checkReadFromStash(ctx context.Context, assert assert.Manager, rnd *rand.Rand, s *stash.Client, exp []data) {
	for _, expected := range exp {
		e, err := s.Lookup(ctx, expected.id)
		assert.For("lookup err").ThatError(err).Succeeded()
		assert.For("lookup e").That(e).IsNotNil()
		assert.For("lookup length").That(e.Length).Equals(int64(len(expected.bytes)))

		rs, err := s.Open(ctx, expected.id)
		assert.For("open").ThatError(err).Succeeded()

		data, err := ioutil.ReadAll(rs)
		assert.For("readall").ThatError(err).Succeeded()
		assert.For("readall").ThatSlice(data).Equals(expected.bytes)

		data, err = readInRandomOrder(rnd, rs, 60, 3)
		assert.For("read random").ThatError(err).Succeeded()
		assert.For("read random").ThatSlice(data).Equals(expected.bytes)

		data, err = s.Read(ctx, expected.id)
		assert.For("stash.Read").ThatError(err).Succeeded()
		assert.For("stash.Read").ThatSlice(data).Equals(expected.bytes)
	}
}

func uploadRandomDataToStash(ctx context.Context, assert assert.Manager, r *rand.Rand, s *stash.Client, lens []int) []data {
	out := make([]data, len(lens))
	for i, ln := range lens {
		bytes := randomBytes(r, ln)
		id, err := s.UploadBytes(ctx, stash.Upload{Name: []string{fmt.Sprintf("%d.bin", i)}, Type: []string{"application/binary"}}, bytes)
		assert.For("upload").ThatError(err).Succeeded()
		out[i] = data{id, bytes}
	}
	return out
}

func TestMemoryAndGrpcStash(t *testing.T) {
	assert := assert.Context(t)
	ctx := log.Testing(t)
	r := rand.New(rand.NewSource(12345))

	memStash := local.NewMemoryService()
	testData := uploadRandomDataToStash(ctx, assert, r, memStash, []int{10, 100, 100, 1, 100000, 100000, 0})
	assert.For("test data length").ThatInteger(len(testData)).IsAtLeast(1)
	checkReadFromStash(ctx, assert, r, memStash, testData)

	var srv *grpc.Server
	go grpcutil.ServeWithListener(
		ctx,
		grpcutil.NewPipeListener("pipe:stashgrpc"),
		func(ctx context.Context, listener net.Listener, server *grpc.Server) error {
			srv = server
			return stashgrpc.Serve(ctx, server, memStash)
		},
	)

	ok := fault.Const("ok")
	err := grpcutil.Client(ctx, "pipe:stashgrpc", func(ctx context.Context, cc *grpc.ClientConn) error {
		sc := stashgrpc.MustConnect(ctx, cc)
		// Make sure the in-memory stash is correctly exposed.
		checkReadFromStash(ctx, assert, r, sc, testData)
		// Make sure upload works through GRPC as well.
		testData = uploadRandomDataToStash(ctx, assert, r, sc, []int{17})
		checkReadFromStash(ctx, assert, r, sc, testData)
		return ok
	}, grpc.WithDialer(grpcutil.GetDialer(ctx)), grpc.WithTimeout(1*time.Second), grpc.WithInsecure())
	assert.For("").ThatError(err).Equals(ok)

	srv.GracefulStop()
}

func randomBytes(r *rand.Rand, length int) []byte {
	p := make([]byte, length)
	r.Read(p)
	return p
}

func TestReadInRandomOrder(t *testing.T) {
	assert := assert.Context(t)
	r := rand.New(rand.NewSource(12345))

	for _, c := range []struct {
		length         int
		sections       int
		maxConsecutive int
	}{
		{1000, 40, 5},
		{1000, 1, 1},
		{1000, 1, 30},
		{1000, 40, 1},
		{1, 1, 1},
	} {
		data := randomBytes(r, c.length)
		got, err := readInRandomOrder(r, bytes.NewReader(data), c.sections, c.maxConsecutive)
		assert.For("read random").ThatError(err).Succeeded()
		assert.For("read random").ThatSlice(got).Equals(data)
	}
}

// readInRandom order exercises an io.ReadSeeker by splitting it up in sections which are
// read in random order. Within a section, several consecutive reads may be performed.
// All types of seeking are used.
func readInRandomOrder(r *rand.Rand, rs io.ReadSeeker, sections int, maxConsecutiveReads int) ([]byte, error) {
	length, err := rs.Seek(0, io.SeekEnd)
	if err != nil {
		return nil, err
	}
	var pos int64 = length

	data := make([]byte, length)

	for _, s := range randomSections(r, int(length), sections, maxConsecutiveReads) {
		switch r.Intn(3) {
		case 0:
			pos, err = rs.Seek(s.offset, io.SeekStart)
		case 1:
			pos, err = rs.Seek(s.offset-length, io.SeekEnd)
		case 2:
			pos, err = rs.Seek(s.offset-pos, io.SeekCurrent)
		}
		if err != nil {
			return nil, err
		}
		if pos != s.offset {
			return nil, errors.New("Incorrect offset returned")
		}
		for _, rd := range s.reads {
			n, err := io.ReadAtLeast(rs, data[int(pos):int(pos)+rd], rd)
			if err != nil {
				return nil, err
			}
			if n != rd {
				return nil, fmt.Errorf("Expected %d bytes to be read, got %d", rd, n)
			}
			pos += int64(n)
		}
	}
	return data, nil
}

type readSection struct {
	offset int64 // absolute section offset
	reads  []int // lengths of consecutive reads within section
}

func randomSections(r *rand.Rand, length int, n int, maxReadsPerSection int) []readSection {
	if length == 0 {
		return []readSection{{0, []int{0}}}
	}
	min := func(a, b int) int {
		if a < b {
			return a
		}
		return b
	}
	sections := make([]readSection, n)
	p := r.Perm(n)
	n = min(n, length)
	ch := randomChunks(r, length, n)
	offset := 0
	for i := 0; i < n; i++ {
		sections[p[i]] = readSection{
			offset: int64(offset),
			reads:  randomChunks(r, ch[i], min(ch[i], 1+r.Intn(maxReadsPerSection))),
		}
		offset += ch[i]
	}
	return sections
}

func randomChunks(r *rand.Rand, length int, n int) []int {
	chunks := make([]int, n)

	sum := 0
	for i := 0; i < n; i++ {
		sz := r.Intn(length / n)
		chunks[i] = sz
		sum += sz
	}
	delta := (length - sum) / n
	for i := 0; i < len(chunks); i++ {
		chunks[i] += delta
		sum += delta
	}
	chunks[r.Intn(len(chunks))] += (length - sum)
	return chunks
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	ch := make(chan string)
	go f(ch)
	x := source()
	ch <- x
}

func f(c chan string) {
	y := <-c
	sink(y)
	a := "Hello Gopher"
	b := g(a)
	fmt.Printf(" %s\n", b)
}

func g(s string) string {
	t := s + " 1"
	return t
}

func sink(s string) {
	fmt.Printf("A gopher reaches a sink: %s \n", s)
}

func source() string {
	return "I am an evil gopher"
}
<commit_msg>Correct spelling<commit_after>package main

import "fmt"

func main() {
	ch := make(chan string)
	go f(ch)
	x := source()
	ch <- x
}

func f(c chan string) {
	y := <-c
	sink(y)
	a := "Hello Gopher"
	b := g(a)
	fmt.Printf(" %s\n", b)
}

func g(s string) string {
	t := s + " 1"
	return t
}

func sink(s string) {
	fmt.Printf("An gopher reaches a sink: %s \n", s)
}

func source() string {
	return "I am an evil gopher"
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strconv"
	"strings"

	"github.com/ajm188/advent_of_code/pkg/cli"
)

func main() {
	path := flag.String("path", "input.txt", "")
	flag.Parse()

	data, err := cli.GetInput(*path)
	cli.ExitOnError(err)

	lines := strings.Split(string(data), "\n")
	depths := make([]int64, 0, len(lines)-1)

	for i, line := range lines {
		if line == "" {
			continue
		}

		val, err := strconv.ParseInt(line, 10, 64)
		if err != nil {
			cli.ExitOnError(fmt.Errorf("input line %d: %w", i, err))
		}

		depths = append(depths, val)
	}

	var numIncreases int
	for i := 1; i < len(depths); i++ {
		if depths[i] > depths[i-1] {
			numIncreases++
		}
	}

	fmt.Printf("%d\n", numIncreases)

	numIncreases = 0
	for i := 0; i < len(depths)-3; i++ {
		prevSum := depths[i] + depths[i+1] + depths[i+2]
		sum := depths[i+1] + depths[i+2] + depths[i+3]
		if sum > prevSum {
			numIncreases++
		}
	}

	fmt.Printf("%d\n", numIncreases)
}
<commit_msg>refactor and generalize to any offset<commit_after>package main

import (
	"flag"
	"fmt"
	"strconv"
	"strings"

	"github.com/ajm188/advent_of_code/pkg/cli"
)

// offsetSumsWhere returns the count of offset sums in xs meeting the condition
// function.
//
// offset sums for a slice are a partition of xs into two slices.
// LHS: [sum(xs[0:n]), sum(xs[1:n+1]), ..., sum(xs[len(xs)-n-1:len(xs)-1])]
// RHS: [sum(xs[1:n]), sum(xs[2:n+2]), ..., sum(xs[len(xs)-n:])]
func offsetSumsWhere(xs []int, n int, f func(l, r int) bool) (count int) {
	l := make([]int, len(xs)-n)
	r := make([]int, len(xs)-n)

	for i, j := 0, 1; i < len(xs)-n && j < len(xs); i, j = i+1, j+1 {
		for k := 0; k < n; k++ {
			l[i] += xs[i+k]
			r[i] += xs[j+k]
		}
	}

	for i := 0; i < len(l); i++ {
		if f(l[i], r[i]) {
			count++
		}
	}

	return count
}

func main() {
	path := flag.String("path", "input.txt", "")
	flag.Parse()

	data, err := cli.GetInput(*path)
	cli.ExitOnError(err)

	lines := strings.Split(string(data), "\n")
	depths := make([]int, 0, len(lines)-1)

	for i, line := range lines {
		if line == "" {
			continue
		}

		val, err := strconv.ParseInt(line, 10, 64)
		if err != nil {
			cli.ExitOnError(fmt.Errorf("input line %d: %w", i, err))
		}

		depths = append(depths, int(val))
	}

	for _, n := range []int{1, 3} {
		fmt.Println(offsetSumsWhere(depths, n, func(l, r int) bool {
			return l < r
		}))
	}
}
<|endoftext|>


<commit_before><commit_msg>Added support for URL params in HandlerFuncs<commit_after><|endoftext|>


<commit_before>package iface

import (
	"log"
	"time"
)

type WeatherCode int

const (
	CodeUnknown WeatherCode = iota
	CodeCloudy
	CodeFog
	CodeHeavyRain
	CodeHeavyShowers
	CodeHeavySnow
	CodeHeavySnowShowers
	CodeLightRain
	CodeLightShowers
	CodeLightSleet
	CodeLightSleetShowers
	CodeLightSnow
	CodeLightSnowShowers
	CodePartlyCloudy
	CodeSunny
	CodeThunderyHeavyRain
	CodeThunderyShowers
	CodeThunderySnowShowers
	CodeVeryCloudy
)

type Cond struct {
	// Time is the time, where this weather condition applies.
	Time time.Time

	// Code is the general weather condition and must be one the WeatherCode
	// constants.
	Code WeatherCode

	// Desc is a short string describing the condition. It should be just one
	// sentence.
	Desc string

	// TempC is the temperature in degrees celsius.
	TempC *float32

	// FeelsLikeC is the felt temperature (with windchill effect e.g.) in
	// degrees celsius.
	FeelsLikeC *float32

	// ChanceOfRainPercent is the probability of rain or snow. It must be in the
	// range [0, 100].
	ChanceOfRainPercent *int

	// PrecipM is the precipitation amount in meters(!). It must be >= 0.
	PrecipM *float32

	// VisibleDistM is the visibility range in meters(!). It must be >= 0.
	VisibleDistM *float32

	// WindspeedKmph is the average wind speed in kilometers per second.
	WindspeedKmph *float32

	// WindGustKmph is the maximum temporary wind speed in kilometers per
	// second. It should be > WindspeedKmph.
	WindGustKmph *float32

	// WinddirDegree is the direction the wind is blowing from on a clock
	// oriented circle with 360 degrees. 0 means the wind is blowing from north,
	// 90 means the wind is blowing from east, 180 means the wind is blowing
	// from south and 270 means the wind is blowing from west. The value must be
	// in the range [0, 359].
	WinddirDegree *int
}

type Astro struct {
	Moonrise time.Time
	Moonset  time.Time
	Sunrise  time.Time
	Sunset   time.Time
}

type Day struct {
	// Date is the date of this Day.
	Date time.Time

	// MaxtempC is the maximum temperature on that day in degrees celsius.
	MaxtempC *float32

	// MintempC is the minimum temperature on that day in degrees celsius.
	MintempC *float32

	// Slots is a slice of conditions for different times of day. They should be
	// ordered by the contained Time field.
	Slots []Cond

	// Astronomy contains planetary data.
	Astronomy Astro
}

type LatLon struct {
	Latitude  float32
	Longitude float32
}

type Data struct {
	Current  Cond
	Forecast []Day
	Location string
	GeoLoc   *LatLon
}

type UnitSystem int

const (
	UnitsMetric UnitSystem = iota
	UnitsImperial
	UnitsSi
)

func (u UnitSystem) Temp(tempC float32) (res float32, unit string) {
	if u == UnitsMetric {
		return tempC, "°C"
	} else if u == UnitsImperial {
		return tempC*1.8 + 32, "°F"
	} else if u == UnitsSi {
		return tempC + 273.16, "°K"
	}
	log.Fatalln("Unknown unit system:", u)
	return
}

func (u UnitSystem) Speed(spdKmph float32) (res float32, unit string) {
	if u == UnitsMetric {
		return spdKmph, "km/h"
	} else if u == UnitsImperial {
		return spdKmph / 1.609, "mph"
	} else if u == UnitsSi {
		return spdKmph / 3.6, "m/s"
	}
	log.Fatalln("Unknown unit system:", u)
	return
}

func (u UnitSystem) Distance(distM float32) (res float32, unit string) {
	if u == UnitsMetric || u == UnitsSi {
		if distM < 1 {
			return distM * 1000, "mm"
		} else if distM < 1000 {
			return distM, "m"
		} else {
			return distM / 1000, "km"
		}
	} else if u == UnitsImperial {
		res, unit = distM/0.0254, "in"
		if res < 3*12 { // 1yd = 3ft, 1ft = 12in
			return
		} else if res < 8*10*22*36 { //1mi = 8fur, 1fur = 10ch, 1ch = 22yd
			return res / 36, "yd"
		} else {
			return res / 8 / 10 / 22 / 36, "mi"
		}
	}
	log.Fatalln("Unknown unit system:", u)
	return
}

type Backend interface {
	Setup()
	Fetch(location string, numdays int) Data
}

type Frontend interface {
	Setup()
	Render(weather Data, unitSystem UnitSystem)
}

var (
	AllBackends  = make(map[string]Backend)
	AllFrontends = make(map[string]Frontend)
)
<commit_msg>iface documentation fix<commit_after>package iface

import (
	"log"
	"time"
)

type WeatherCode int

const (
	CodeUnknown WeatherCode = iota
	CodeCloudy
	CodeFog
	CodeHeavyRain
	CodeHeavyShowers
	CodeHeavySnow
	CodeHeavySnowShowers
	CodeLightRain
	CodeLightShowers
	CodeLightSleet
	CodeLightSleetShowers
	CodeLightSnow
	CodeLightSnowShowers
	CodePartlyCloudy
	CodeSunny
	CodeThunderyHeavyRain
	CodeThunderyShowers
	CodeThunderySnowShowers
	CodeVeryCloudy
)

type Cond struct {
	// Time is the time, where this weather condition applies.
	Time time.Time

	// Code is the general weather condition and must be one the WeatherCode
	// constants.
	Code WeatherCode

	// Desc is a short string describing the condition. It should be just one
	// sentence.
	Desc string

	// TempC is the temperature in degrees celsius.
	TempC *float32

	// FeelsLikeC is the felt temperature (with windchill effect e.g.) in
	// degrees celsius.
	FeelsLikeC *float32

	// ChanceOfRainPercent is the probability of rain or snow. It must be in the
	// range [0, 100].
	ChanceOfRainPercent *int

	// PrecipM is the precipitation amount in meters(!) per hour. Must be >= 0.
	PrecipM *float32

	// VisibleDistM is the visibility range in meters(!). It must be >= 0.
	VisibleDistM *float32

	// WindspeedKmph is the average wind speed in kilometers per hour. The value
	// must be >= 0.
	WindspeedKmph *float32

	// WindGustKmph is the maximum temporary wind speed in kilometers per
	// second. It should be > WindspeedKmph.
	WindGustKmph *float32

	// WinddirDegree is the direction the wind is blowing from on a clock
	// oriented circle with 360 degrees. 0 means the wind is blowing from north,
	// 90 means the wind is blowing from east, 180 means the wind is blowing
	// from south and 270 means the wind is blowing from west. The value must be
	// in the range [0, 359].
	WinddirDegree *int
}

type Astro struct {
	Moonrise time.Time
	Moonset  time.Time
	Sunrise  time.Time
	Sunset   time.Time
}

type Day struct {
	// Date is the date of this Day.
	Date time.Time

	// MaxtempC is the maximum temperature on that day in degrees celsius.
	MaxtempC *float32

	// MintempC is the minimum temperature on that day in degrees celsius.
	MintempC *float32

	// Slots is a slice of conditions for different times of day. They should be
	// ordered by the contained Time field.
	Slots []Cond

	// Astronomy contains planetary data.
	Astronomy Astro
}

type LatLon struct {
	Latitude  float32
	Longitude float32
}

type Data struct {
	Current  Cond
	Forecast []Day
	Location string
	GeoLoc   *LatLon
}

type UnitSystem int

const (
	UnitsMetric UnitSystem = iota
	UnitsImperial
	UnitsSi
)

func (u UnitSystem) Temp(tempC float32) (res float32, unit string) {
	if u == UnitsMetric {
		return tempC, "°C"
	} else if u == UnitsImperial {
		return tempC*1.8 + 32, "°F"
	} else if u == UnitsSi {
		return tempC + 273.16, "°K"
	}
	log.Fatalln("Unknown unit system:", u)
	return
}

func (u UnitSystem) Speed(spdKmph float32) (res float32, unit string) {
	if u == UnitsMetric {
		return spdKmph, "km/h"
	} else if u == UnitsImperial {
		return spdKmph / 1.609, "mph"
	} else if u == UnitsSi {
		return spdKmph / 3.6, "m/s"
	}
	log.Fatalln("Unknown unit system:", u)
	return
}

func (u UnitSystem) Distance(distM float32) (res float32, unit string) {
	if u == UnitsMetric || u == UnitsSi {
		if distM < 1 {
			return distM * 1000, "mm"
		} else if distM < 1000 {
			return distM, "m"
		} else {
			return distM / 1000, "km"
		}
	} else if u == UnitsImperial {
		res, unit = distM/0.0254, "in"
		if res < 3*12 { // 1yd = 3ft, 1ft = 12in
			return
		} else if res < 8*10*22*36 { //1mi = 8fur, 1fur = 10ch, 1ch = 22yd
			return res / 36, "yd"
		} else {
			return res / 8 / 10 / 22 / 36, "mi"
		}
	}
	log.Fatalln("Unknown unit system:", u)
	return
}

type Backend interface {
	Setup()
	Fetch(location string, numdays int) Data
}

type Frontend interface {
	Setup()
	Render(weather Data, unitSystem UnitSystem)
}

var (
	AllBackends  = make(map[string]Backend)
	AllFrontends = make(map[string]Frontend)
)
<|endoftext|>


<commit_before><commit_msg>Cleanup of issuer.go.<commit_after><|endoftext|>