<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/MichaelTJones/walk"
	"github.com/briandowns/spinner"
)

var (
	spinSet  = []string{"| ", "/ ", "- ", "\\ "}
	spin     = spinner.New(spinSet, 100*time.Millisecond)
	repodirs = make(sort.StringSlice, 0)
	startdir = "."
	mutex    = sync.Mutex{}
)

func init() {
	if len(os.Args[1:]) > 0 {
		startdir = filepath.Base(os.Args[1])
	}
}

func foreachEntry(entryName string, f os.FileInfo, err error) error {
	if f == nil {
		// Just ignore file errors from system
		return nil
	}

	if !f.IsDir() {
		// Ignore files
		return nil
	}

	if f.Name() != ".git" {
		// Ignore everything except .git repositories
		return nil
	}

	var dir = strings.Replace(entryName, ".git", "", 1)

	// if not found, just ignore
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return nil
	}

	mutex.Lock()
	repodirs = append(repodirs, dir)
	mutex.Unlock()

	return nil
}

func printStatus(workdir string) {
	var gitdir = workdir + ".git"
	var cmd = exec.Command("git", "--git-dir="+gitdir, "--work-tree="+workdir, "status", "-s")

	var r, w, _ = os.Pipe()
	cmd.Stdout = w
	cmd.Stderr = os.Stderr
	cmd.Run()
	w.Close()

	var status, _ = ioutil.ReadAll(r)

	if len(bytes.TrimSpace(status)) > 0 {
		var workpath, err = filepath.Abs(workdir)
		if err != nil {
			workpath = workdir
		}

		fmt.Println(workpath)
		fmt.Println(string(status))
	}
}

func main() {
	spin.Start()
	walk.Walk(startdir, foreachEntry)
	spin.Restart()
	spin.Stop()

	sort.Sort(repodirs)
	for _, wdir := range repodirs {
		printStatus(wdir)
	}
}
<commit_msg>removing some comments<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/MichaelTJones/walk"
	"github.com/briandowns/spinner"
)

var (
	spinSet  = []string{"| ", "/ ", "- ", "\\ "}
	spin     = spinner.New(spinSet, 100*time.Millisecond)
	repodirs = make(sort.StringSlice, 0)
	startdir = "."
	mutex    = sync.Mutex{}
)

func init() {
	if len(os.Args[1:]) > 0 {
		startdir = filepath.Base(os.Args[1])
	}
}

func foreachEntry(entryName string, f os.FileInfo, err error) error {
	if f == nil {
		return nil
	}

	if !f.IsDir() {
		return nil
	}

	if f.Name() != ".git" {
		return nil
	}

	// Getting the working dir
	var dir = strings.Replace(entryName, ".git", "", 1)

	mutex.Lock()
	repodirs = append(repodirs, dir)
	mutex.Unlock()

	return nil
}

func printStatus(workdir string) {
	var gitdir = workdir + ".git"
	var cmd = exec.Command("git", "--git-dir="+gitdir, "--work-tree="+workdir, "status", "-s")

	var r, w, _ = os.Pipe()
	cmd.Stdout = w
	cmd.Stderr = os.Stderr
	cmd.Run()
	w.Close()

	var status, _ = ioutil.ReadAll(r)

	if len(bytes.TrimSpace(status)) > 0 {
		var workpath, err = filepath.Abs(workdir)
		if err != nil {
			workpath = workdir
		}

		fmt.Println(workpath)
		fmt.Println(string(status))
	}
}

func main() {
	spin.Start()
	walk.Walk(startdir, foreachEntry)
	spin.Restart()
	spin.Stop()

	sort.Sort(repodirs)
	for _, wdir := range repodirs {
		printStatus(wdir)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"github.com/oschwald/geoip2-golang"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strconv"
)

var db *sql.DB
var visitorsStmt *sql.Stmt
var visitStmt *sql.Stmt

type Visit struct {
	timse    string
	location string
	ip       string
}

func handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" || r.Method == "" {
		get(w)
	} else if r.Method == "POST" {
		post(w, r)
	}
}

func get(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	rows, err := db.Query("select count(id), strftime(\"%Y-%m-%d %H:00:00\", datetime(time, 'localtime')) from visits where time > datetime('now', '-500 hours') group by strftime(\"%Y%j%H\", time);")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	result := map[string][]map[string]string{}
	counts := []map[string]string{}
	for rows.Next() {
		var count string
		var time string

		rows.Scan(&count, &time)
		counts = append(counts, map[string]string{
			"time":  time,
			"count": count,
		})
	}
	result["counts"] = counts

	lrows, err := db.Query("select count(city), city, country, iso from visitors group by city, iso;")
	if err != nil {
		log.Fatal(err)
	}
	defer lrows.Close()
	locations := []map[string]string{}
	for lrows.Next() {
		var count string
		var city string
		var country string
		var iso string

		lrows.Scan(&count, &city, &country, &iso)
		locations = append(locations, map[string]string{
			"city": city,
			"country": country,
			"iso": iso,
			"count": count,
		})
	}
	result["locations"] = locations

	b, _ := json.Marshal(result)
	fmt.Fprintf(w, string(b))

	rows.Close()
}

func post(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	if r.FormValue("action") == "enter" {
		var id int64
		avid := r.FormValue("avid")

		if avid == "" {
			host, _, _ := net.SplitHostPort(r.RemoteAddr)
			if host != "" {
				gr := geo(host)
				result, err := visitorsStmt.Exec(gr["city"], gr["country"], gr["iso"], host)

				if err != nil {
					log.Print(err)
					return
				}

				id, _ = result.LastInsertId()
				response := map[string]string{}
				response["vid"] = strconv.FormatInt(id, 10)

				rj, _ := json.Marshal(response)
				fmt.Fprintf(w, string(rj))
			}
		} else {
			id_s, _ := strconv.Atoi(avid)
			id = int64(id_s)
		}

		_, err := visitStmt.Exec(r.FormValue("url"), r.FormValue("referrer"), id)

		if err != nil {
			log.Print(err)
			return
		}
	}
}

func geo(ipstring string) map[string]string {
	db, err := geoip2.Open("GeoLite2-City.mmdb")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	ip := net.ParseIP(ipstring)
	if ip != nil {
		record, err := db.City(ip)
		if err != nil {
			log.Fatal(err)
		}
		return map[string]string{
			"city":    record.City.Names["en"],
			"country": record.Country.Names["en"],
			"iso":     record.Country.IsoCode,
		}
	}

	return map[string]string{
		"city":    "",
		"country": "",
		"iso":     "",
	}
}

func update(w http.ResponseWriter, r *http.Request) {
	cmd := exec.Command("update")
	err := cmd.Start()
	if err != nil {
		fmt.Println(err)
	} else {
		os.Exit(0);
	}
}

func main() {
	isNew := false

	_, err := os.Open("./alight.db")
	if err != nil {
		isNew = true
	}

	db, err = sql.Open("sqlite3", "./alight.db")
	defer db.Close()

	if isNew {
		sqlStmt := `
		create table visits (id integer primary key, url text, time integer, referrer text, vid integer, foreign key(vid) references visitors(vid));
		create table visitors (vid integer primary key, city text, country text, iso text, ip text);
		`

		_, err = db.Exec(sqlStmt)
		if err != nil {
			os.Remove("./alight.db")
			log.Printf("%q: %s\n", err, sqlStmt)
			return
		}
	}

	db.Exec("pragma synchronous = OFF")

	visitorsStmt, err = db.Prepare("insert into visitors values (null, ?, ?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	visitStmt, err = db.Prepare("insert into visits values (null, ?, datetime('now'), ?, ?);")
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/update", update)
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8000", nil)
}
<commit_msg>write error to http output<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"github.com/oschwald/geoip2-golang"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strconv"
)

var db *sql.DB
var visitorsStmt *sql.Stmt
var visitStmt *sql.Stmt

type Visit struct {
	timse    string
	location string
	ip       string
}

func handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" || r.Method == "" {
		get(w)
	} else if r.Method == "POST" {
		post(w, r)
	}
}

func get(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	rows, err := db.Query("select count(id), strftime(\"%Y-%m-%d %H:00:00\", datetime(time, 'localtime')) from visits where time > datetime('now', '-500 hours') group by strftime(\"%Y%j%H\", time);")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	result := map[string][]map[string]string{}
	counts := []map[string]string{}
	for rows.Next() {
		var count string
		var time string

		rows.Scan(&count, &time)
		counts = append(counts, map[string]string{
			"time":  time,
			"count": count,
		})
	}
	result["counts"] = counts

	lrows, err := db.Query("select count(city), city, country, iso from visitors group by city, iso;")
	if err != nil {
		log.Fatal(err)
	}
	defer lrows.Close()
	locations := []map[string]string{}
	for lrows.Next() {
		var count string
		var city string
		var country string
		var iso string

		lrows.Scan(&count, &city, &country, &iso)
		locations = append(locations, map[string]string{
			"city": city,
			"country": country,
			"iso": iso,
			"count": count,
		})
	}
	result["locations"] = locations

	b, _ := json.Marshal(result)
	fmt.Fprintf(w, string(b))

	rows.Close()
}

func post(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	if r.FormValue("action") == "enter" {
		var id int64
		avid := r.FormValue("avid")

		if avid == "" {
			host, _, _ := net.SplitHostPort(r.RemoteAddr)
			if host != "" {
				gr := geo(host)
				result, err := visitorsStmt.Exec(gr["city"], gr["country"], gr["iso"], host)

				if err != nil {
					log.Print(err)
					return
				}

				id, _ = result.LastInsertId()
				response := map[string]string{}
				response["vid"] = strconv.FormatInt(id, 10)

				rj, _ := json.Marshal(response)
				fmt.Fprintf(w, string(rj))
			}
		} else {
			id_s, _ := strconv.Atoi(avid)
			id = int64(id_s)
		}

		_, err := visitStmt.Exec(r.FormValue("url"), r.FormValue("referrer"), id)

		if err != nil {
			log.Print(err)
			return
		}
	}
}

func geo(ipstring string) map[string]string {
	db, err := geoip2.Open("GeoLite2-City.mmdb")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	ip := net.ParseIP(ipstring)
	if ip != nil {
		record, err := db.City(ip)
		if err != nil {
			log.Fatal(err)
		}
		return map[string]string{
			"city":    record.City.Names["en"],
			"country": record.Country.Names["en"],
			"iso":     record.Country.IsoCode,
		}
	}

	return map[string]string{
		"city":    "",
		"country": "",
		"iso":     "",
	}
}

func update(w http.ResponseWriter, r *http.Request) {
	cmd := exec.Command("update")
	err := cmd.Start()
	if err != nil {
		fmt.Fprintln(w,err)
	} else {
		os.Exit(0);
	}
}

func main() {
	isNew := false

	_, err := os.Open("./alight.db")
	if err != nil {
		isNew = true
	}

	db, err = sql.Open("sqlite3", "./alight.db")
	defer db.Close()

	if isNew {
		sqlStmt := `
		create table visits (id integer primary key, url text, time integer, referrer text, vid integer, foreign key(vid) references visitors(vid));
		create table visitors (vid integer primary key, city text, country text, iso text, ip text);
		`

		_, err = db.Exec(sqlStmt)
		if err != nil {
			os.Remove("./alight.db")
			log.Printf("%q: %s\n", err, sqlStmt)
			return
		}
	}

	db.Exec("pragma synchronous = OFF")

	visitorsStmt, err = db.Prepare("insert into visitors values (null, ?, ?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	visitStmt, err = db.Prepare("insert into visits values (null, ?, datetime('now'), ?, ?);")
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/update", update)
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8000", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/hut8/tumblr-go"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"html/template"
	"net/http"
	"os"
)

var upgrader websocket.Upgrader

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("templates/index.go.html")
	ensureNil(err)
	err = t.Execute(w, nil)
	ensureNil(err)
}

type SimilarPostRequest struct {
	PostUri string
}

type beginNotification struct {
	BaseHostname string `json:"base-hostname"`
	PostID       int64  `json:"pid"`
	MsgType      string `json:"msg-type"`
}

func sendBeginNotification(c *websocket.Conn, bh string, pid int64) error {
	msg := &beginNotification{
		BaseHostname: bh,
		PostID:       pid,
		MsgType:      "begin-notification",
	}
	return c.WriteJSON(msg)
}

func sendErrorNotification(c *websocket.Conn, err error) error {
	return c.WriteJSON(&struct {
		MsgType string `json:"msg-type"`
		Message string `json:"message"`
	}{
		"error",
		err.Error(),
	})
}

func sendBlogsLikingPostData(c *websocket.Conn, blogs []string) error {
	return c.WriteJSON(&struct {
		MsgType string   `json:"msg-type"`
		Blogs   []string `json:"blogs"`
	}{
		"blogs-liking-post",
		blogs,
	})
}

func sendBlogLikesData(c *websocket.Conn, blog string, likes []string) error {
	return c.WriteJSON(&struct {
		MsgType string   `json:"msg-type"`
		Blog    string   `json:"blog"`
		Likes   []string `json:"likes"`
	}{
		"blog-likes",
		blog,
		likes,
	})
}

func SimilarHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	ensureNil(err)
	defer conn.Close()

	// Figure out what post they want and extract the details we need
	spr := &SimilarPostRequest{}
	err = conn.ReadJSON(spr)
	ensureNil(err)
	bh, pid, err := extractPostId(spr.PostUri)
	if err != nil {
		msg := fmt.Errorf("invalid post uri: %s", spr.PostUri)
		sendErrorNotification(conn, msg)
		return
	}

	err = sendBeginNotification(conn, bh, pid)
	ensureNil(err)

	// Find every blog that likes the input post
	likingBlogs, err := blogsLikingPost(bh, pid)
	ensureNil(err)
	err = sendBlogsLikingPostData(conn, likingBlogs)
	ensureNil(err)

	// Find every liked post from every blog that likes the input post
	// postId -> []blogUrl
	popularityMap := make(map[int64][]string)
	// postId -> Post
	postMap := make(map[int64]tumblr.Post)
	for _, blogName := range likingBlogs {
		b := tumblrClient.NewBlog(blogName)
		fmt.Printf("Requesting likes for: %s\n", b.BaseHostname)
		// TODO Loop over all the pages here
		likeCollection, err := b.Likes(tumblr.LimitOffset{})
		if err != nil {
			fmt.Println(err.Error())
			continue
		}
		for _, likedPost := range likeCollection.Likes.Posts {
			postMap[likedPost.PostId()] = likedPost
			// Initialize if key if needbe
			_, ok := popularityMap[likedPost.PostId()]
			if !ok {
				popularityMap[likedPost.PostId()] = []string{}
			}
			popularityMap[likedPost.PostId()] = append(
				popularityMap[likedPost.PostId()], b.BaseHostname)
		}
		fmt.Printf("%#v\n", popularityMap)
		//sendBlogsLikingPostData(conn, popularityMap[likedPost.PostId()])
	}
}

func ensureNil(x interface{}) {
	if x != nil {
		panic(x)
	}
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", HomeHandler)
	router.HandleFunc("/post", SimilarHandler)

	n := negroni.New()
	n.UseHandler(router)
	n.Run(fmt.Sprintf(":%s", os.Getenv("PORT")))
}
<commit_msg>gofmt, add pretty printing for debugging.  return to client PIDs of a blog's liked posts<commit_after>package main

import (
	"fmt"
	//	"github.com/bradfitz/iter"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/hut8/tumblr-go"
	"github.com/kr/pretty"
	"html/template"
	"net/http"
	"os"
)

var upgrader websocket.Upgrader

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("templates/index.go.html")
	ensureNil(err)
	err = t.Execute(w, nil)
	ensureNil(err)
}

type SimilarPostRequest struct {
	PostUri string
}

type beginNotification struct {
	BaseHostname string `json:"base-hostname"`
	PostID       int64  `json:"pid"`
	MsgType      string `json:"msg-type"`
}

func sendBeginNotification(c *websocket.Conn, bh string, pid int64) error {
	msg := &beginNotification{
		BaseHostname: bh,
		PostID:       pid,
		MsgType:      "begin-notification",
	}
	return c.WriteJSON(msg)
}

func sendErrorNotification(c *websocket.Conn, err error) error {
	return c.WriteJSON(&struct {
		MsgType string `json:"msg-type"`
		Message string `json:"message"`
	}{
		"error",
		err.Error(),
	})
}

func sendBlogsLikingPostData(c *websocket.Conn, blogs []string) error {
	return c.WriteJSON(&struct {
		MsgType string   `json:"msg-type"`
		Blogs   []string `json:"blogs"`
	}{
		"blogs-liking-post",
		blogs,
	})
}

func sendBlogLikesData(c *websocket.Conn, blog string, likes []int64) error {
	return c.WriteJSON(&struct {
		MsgType string  `json:"msg-type"`
		Blog    string  `json:"blog"`
		Likes   []int64 `json:"likes"`
	}{
		"blog-likes",
		blog,
		likes,
	})
}

func SimilarHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	ensureNil(err)
	defer conn.Close()

	// Figure out what post they want and extract the details we need
	spr := &SimilarPostRequest{}
	err = conn.ReadJSON(spr)
	ensureNil(err)
	bh, pid, err := extractPostId(spr.PostUri)
	if err != nil {
		msg := fmt.Errorf("invalid post uri: %s", spr.PostUri)
		sendErrorNotification(conn, msg)
		return
	}

	err = sendBeginNotification(conn, bh, pid)
	ensureNil(err)

	// Find every blog that likes the input post
	likingBlogs, err := blogsLikingPost(bh, pid)
	ensureNil(err)
	err = sendBlogsLikingPostData(conn, likingBlogs)
	ensureNil(err)

	// Find every liked post from every blog that likes the input post
	// postId -> []blogUrl
	popularityMap := make(map[int64][]string)
	// postId -> Post
	postMap := make(map[int64]tumblr.Post)
	for _, blogName := range likingBlogs {
		b := tumblrClient.NewBlog(blogName)
		fmt.Printf("Requesting likes for: %s\n", b.BaseHostname)
		// TODO Loop over all the pages here
		likeCollection, err := b.Likes(tumblr.LimitOffset{})
		if err != nil {
			fmt.Println(err.Error())
			continue
		}
		for _, likedPost := range likeCollection.Likes.Posts {
			postMap[likedPost.PostId()] = likedPost
			// Initialize if key if needbe
			_, ok := popularityMap[likedPost.PostId()]
			if !ok {
				popularityMap[likedPost.PostId()] = []string{}
			}
			popularityMap[likedPost.PostId()] = append(
				popularityMap[likedPost.PostId()], b.BaseHostname)
		}
		fmt.Printf("%#v\n", popularityMap)
		//sendBlogsLikingPostData(conn, popularityMap[likedPost.PostId()])
	}
}

func ensureNil(x interface{}) {
	if x != nil {
		panic(x)
	}
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", HomeHandler)
	router.HandleFunc("/post", SimilarHandler)

	n := negroni.New()
	n.UseHandler(router)
	n.Run(fmt.Sprintf(":%s", os.Getenv("PORT")))
}
<|endoftext|>


<commit_before>// 4 october 2014
package main

import (
	"os"
	"io"
	"io/ioutil"
	"bytes"
	"path/filepath"
)

type Asset struct {
	MSHC	string
	Data		[]byte
}

var assets = make(map[string]*Asset)

func addAsset(mshcname string, name string, r io.Reader) {
	b, err := ioutil.ReadAll(r)
	if err != nil {
		panic(err)		// TODO
	}
	a := &Asset{
		MSHC:	mshcname,
		Data:	b,
	}
	if assets[name] == nil {
		assets[name] = a
		return
	}
	if !bytes.Equal(assets[name].Data, a.Data) {
		panic("duplicate differing assets " + name + ": " + assets[name].MSHC + " vs " + a.MSHC)
	}
}

func copyAssets(dir string) {
	for name, a := range assets {
		f, err := os.Create(filepath.Join(dir, name))
		if err != nil {
			panic(err)		// TODO
		}
		_, err = f.Write(a.Data)
		if err != nil {
			panic(err)		// TODO
		}
		f.Close()
	}
}
<commit_msg>Fixed image embeds for now.<commit_after>// 4 october 2014
package main

import (
	"os"
	"io"
	"io/ioutil"
	"bytes"
	"path/filepath"
	"strings"
)

type Asset struct {
	MSHC	string
	Data		[]byte
}

var assets = make(map[string]*Asset)

func addAsset(mshcname string, name string, r io.Reader) {
	b, err := ioutil.ReadAll(r)
	if err != nil {
		panic(err)		// TODO
	}
	a := &Asset{
		MSHC:	mshcname,
		Data:	b,
	}
	if assets[name] == nil {
		assets[name] = a
		return
	}
	if !bytes.Equal(assets[name].Data, a.Data) {
		panic("duplicate differing assets " + name + ": " + assets[name].MSHC + " vs " + a.MSHC)
	}
}

func copyAssets(dir string) {
	for name, a := range assets {
		// annoyingly the actual <img src="..."> values in the HTML use uppercase
		// TODO if we rewrite HTML in the future, avoid this
		f, err := os.Create(filepath.Join(dir, strings.ToUpper(name)))
		if err != nil {
			panic(err)		// TODO
		}
		_, err = f.Write(a.Data)
		if err != nil {
			panic(err)		// TODO
		}
		f.Close()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"strings"

	"github.com/aerogo/aero"
	"github.com/animenotifier/notify.moe/components/js"
)

// configureAssets adds all the routes used for media assets.
func configureAssets(app *aero.Application) {
	// Script bundle
	scriptBundle := js.Bundle()

	// Service worker
	serviceWorkerBytes, err := ioutil.ReadFile("sw/service-worker.js")
	serviceWorker := string(serviceWorkerBytes)

	if err != nil {
		panic("Couldn't load service worker")
	}

	app.Get("/scripts", func(ctx *aero.Context) string {
		return ctx.JavaScript(scriptBundle)
	})

	app.Get("/scripts.js", func(ctx *aero.Context) string {
		return ctx.JavaScript(scriptBundle)
	})

	app.Get("/service-worker", func(ctx *aero.Context) string {
		return ctx.JavaScript(serviceWorker)
	})

	// Web manifest
	app.Get("/manifest.json", func(ctx *aero.Context) string {
		return ctx.JSON(app.Config.Manifest)
	})

	// Favicon
	app.Get("/favicon.ico", func(ctx *aero.Context) string {
		return ctx.TryWebP("images/brand/64", ".png")
	})

	// Brand icons
	app.Get("/images/brand/:file", func(ctx *aero.Context) string {
		return ctx.File("images/brand/" + ctx.Get("file"))
	})

	// Cover image
	app.Get("/images/cover/:file", func(ctx *aero.Context) string {
		file := strings.TrimSuffix(ctx.Get("file"), ".webp")
		return ctx.TryWebP("images/cover/"+file, ".jpg")
	})

	// Login buttons
	app.Get("/images/login/:file", func(ctx *aero.Context) string {
		return ctx.File("images/login/" + ctx.Get("file") + ".png")
	})

	// Avatars
	app.Get("/images/avatars/large/:file", func(ctx *aero.Context) string {
		return ctx.File("images/avatars/large/" + ctx.Get("file"))
	})

	// Avatars
	app.Get("/images/avatars/small/:file", func(ctx *aero.Context) string {
		return ctx.File("images/avatars/large/" + ctx.Get("file"))
	})

	// Elements
	app.Get("/images/elements/:file", func(ctx *aero.Context) string {
		return ctx.File("images/elements/" + ctx.Get("file"))
	})

	// For benchmarks
	app.Get("/hello", func(ctx *aero.Context) string {
		return ctx.Text("Hello World")
	})
}
<commit_msg>Fixed assets<commit_after>package main

import (
	"io/ioutil"
	"strings"

	"github.com/aerogo/aero"
	"github.com/animenotifier/notify.moe/components/js"
)

// configureAssets adds all the routes used for media assets.
func configureAssets(app *aero.Application) {
	// Script bundle
	scriptBundle := js.Bundle()

	// Service worker
	serviceWorkerBytes, err := ioutil.ReadFile("sw/service-worker.js")
	serviceWorker := string(serviceWorkerBytes)

	if err != nil {
		panic("Couldn't load service worker")
	}

	app.Get("/scripts", func(ctx *aero.Context) string {
		return ctx.JavaScript(scriptBundle)
	})

	app.Get("/scripts.js", func(ctx *aero.Context) string {
		return ctx.JavaScript(scriptBundle)
	})

	app.Get("/service-worker", func(ctx *aero.Context) string {
		return ctx.JavaScript(serviceWorker)
	})

	// Web manifest
	app.Get("/manifest.json", func(ctx *aero.Context) string {
		return ctx.JSON(app.Config.Manifest)
	})

	// Favicon
	app.Get("/favicon.ico", func(ctx *aero.Context) string {
		return ctx.TryWebP("images/brand/64", ".png")
	})

	// Brand icons
	app.Get("/images/brand/:file", func(ctx *aero.Context) string {
		return ctx.File("images/brand/" + ctx.Get("file"))
	})

	// Cover image
	app.Get("/images/cover/:file", func(ctx *aero.Context) string {
		file := strings.TrimSuffix(ctx.Get("file"), ".webp")
		return ctx.TryWebP("images/cover/"+file, ".jpg")
	})

	// Login buttons
	app.Get("/images/login/:file", func(ctx *aero.Context) string {
		return ctx.File("images/login/" + ctx.Get("file") + ".png")
	})

	// Avatars
	app.Get("/images/avatars/large/:file", func(ctx *aero.Context) string {
		return ctx.File("images/avatars/large/" + ctx.Get("file"))
	})

	// Avatars
	app.Get("/images/avatars/small/:file", func(ctx *aero.Context) string {
		return ctx.File("images/avatars/small/" + ctx.Get("file"))
	})

	// Elements
	app.Get("/images/elements/:file", func(ctx *aero.Context) string {
		return ctx.File("images/elements/" + ctx.Get("file"))
	})

	// For benchmarks
	app.Get("/hello", func(ctx *aero.Context) string {
		return ctx.Text("Hello World")
	})
}
<|endoftext|>


<commit_before>package avatar

import (
	"bufio"
	"bytes"
	"fmt"
	"image"
	"image/draw"
	"image/png"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
)

const (
	defaultfontFace = "Roboto-Bold.ttf" //SourceSansVariable-Roman.ttf"
	fontSize        = 210.0
	imageWidth      = 500.0
	imageHeight     = 500.0
	dpi             = 72.0
	spacer          = 20
	textY           = 320
)

var fontFacePath = ""

// SetFontFacePath sets the font to do the business with
func SetFontFacePath(f string) {
	fontFacePath = f
}

// var sourceDir string

// func init() {
// 	// We need to set the source directory for the font
// 	_, filename, _, ok := runtime.Caller(0)
// 	if !ok {
// 		panic("No caller information")
// 	}
// 	sourceDir = path.Dir(filename)
// }

// ToDisk saves the image to disk
func ToDisk(initials, path string) {
	saveToDisk(initials, path, "", "")
}

// ToDiskCustom saves the image to disk
func ToDiskCustom(initials, path, bgColor, fontColor string) {
	saveToDisk(initials, path, bgColor, fontColor)
}

// saveToDisk saves the image to disk
func saveToDisk(initials, path, bgColor, fontColor string) {
	rgba, err := createAvatar(initials, bgColor, fontColor)
	if err != nil {
		log.Println(err)
		return
	}

	// Save image to disk
	out, err := os.Create(path)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
	defer out.Close()

	b := bufio.NewWriter(out)

	err = png.Encode(b, rgba)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	err = b.Flush()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
}

// ToHTTP sends the image to a http.ResponseWriter (as a PNG)
func ToHTTP(initials string, w http.ResponseWriter) {
	saveToHTTP(initials, "", "", w)
}

// ToHTTPCustom sends the image to a http.ResponseWriter (as a PNG)
func ToHTTPCustom(initials, bgColor, fontColor string, w http.ResponseWriter) {
	saveToHTTP(initials, "", "", w)
}

// saveToHTTP sends the image to a http.ResponseWriter (as a PNG)
func saveToHTTP(initials, bgColor, fontColor string, w http.ResponseWriter) {
	rgba, err := createAvatar(initials, bgColor, fontColor)
	if err != nil {
		log.Println(err)
		return
	}

	b := new(bytes.Buffer)
	key := fmt.Sprintf("avatar%s", initials) // for Etag

	err = png.Encode(b, rgba)
	if err != nil {
		log.Println("unable to encode image.")
	}

	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("Content-Length", strconv.Itoa(len(b.Bytes())))
	w.Header().Set("Cache-Control", "max-age=2592000") // 30 days
	w.Header().Set("Etag", `"`+key+`"`)

	if _, err := w.Write(b.Bytes()); err != nil {
		log.Println("unable to write image.")
	}
}

func cleanString(incoming string) string {
	incoming = strings.TrimSpace(incoming)

	// If its something like "firstname surname" get the initials out
	split := strings.Split(incoming, " ")
	if len(split) == 2 {
		incoming = split[0][0:1] + split[1][0:1]
	}

	// Max length of 2
	if len(incoming) > 2 {
		incoming = incoming[0:2]
	}

	// To upper and trimmed
	return strings.ToUpper(strings.TrimSpace(incoming))
}

func getFont(fontPath string) (*truetype.Font, error) {
	if fontPath == "" {
		fontPath = defaultfontFace
	}
	// Read the font data.
	fontBytes, err := ioutil.ReadFile(fontPath) //fmt.Sprintf("%s/%s", sourceDir, fontFaceName))
	if err != nil {
		return nil, err
	}

	return freetype.ParseFont(fontBytes)
}

var imageCache sync.Map

func getImage(initials string) *image.RGBA {
	value, ok := imageCache.Load(initials)

	if !ok {
		return nil
	}

	image, ok2 := value.(*image.RGBA)
	if !ok2 {
		return nil
	}
	return image
}

func setImage(initials string, image *image.RGBA) {
	imageCache.Store(initials, image)
}

func createAvatar(initials, bgColor, fontColor string) (*image.RGBA, error) {
	// Make sure the string is OK
	text := cleanString(initials)

	// Check cache
	cachedImage := getImage(text)
	if cachedImage != nil {
		return cachedImage, nil
	}

	// Load and get the font
	f, err := getFont(fontFacePath)
	if err != nil {
		return nil, err
	}

	// Setup the colors, text white, background based on first initial
	textColor := image.White
	if fontColor != "" {
		c, err := parseHexColorFast(fontColor)
		if err == nil {
			textColor = &image.Uniform{c}
		}
	}
	background := defaultColor(text[0:1])
	if bgColor != "" {
		c, err := parseHexColorFast(bgColor)
		if err == nil {
			background = image.Uniform{c}
		}
	}

	rgba := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))
	draw.Draw(rgba, rgba.Bounds(), &background, image.ZP, draw.Src)
	c := freetype.NewContext()
	c.SetDPI(dpi)
	c.SetFont(f)
	c.SetFontSize(fontSize)
	c.SetClip(rgba.Bounds())
	c.SetDst(rgba)
	c.SetSrc(textColor)
	c.SetHinting(font.HintingFull)

	// We need to convert the font into a "font.Face" so we can read the glyph
	// info
	to := truetype.Options{}
	to.Size = fontSize
	face := truetype.NewFace(f, &to)

	// Calculate the widths and print to image
	xPoints := []int{0, 0}
	textWidths := []int{0, 0}

	// Get the widths of the text characters
	for i, char := range text {
		width, ok := face.GlyphAdvance(rune(char))
		if !ok {
			return nil, err
		}

		textWidths[i] = int(float64(width) / 64)
	}

	// TODO need some tests for this
	if len(textWidths) == 1 {
		textWidths[1] = 0
	}

	// Get the combined width of the characters
	combinedWidth := textWidths[0] + spacer + textWidths[1]

	// Draw first character
	xPoints[0] = int((imageWidth - combinedWidth) / 2)
	xPoints[1] = int(xPoints[0] + textWidths[0] + spacer)

	for i, char := range text {
		pt := freetype.Pt(xPoints[i], textY)
		_, err := c.DrawString(string(char), pt)
		if err != nil {
			return nil, err
		}
	}

	// Cache it
	setImage(text, rgba)

	return rgba, nil
}
<commit_msg>fix bug with custom HTTP handler<commit_after>package avatar

import (
	"bufio"
	"bytes"
	"fmt"
	"image"
	"image/draw"
	"image/png"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
)

const (
	defaultfontFace = "Roboto-Bold.ttf" //SourceSansVariable-Roman.ttf"
	fontSize        = 210.0
	imageWidth      = 500.0
	imageHeight     = 500.0
	dpi             = 72.0
	spacer          = 20
	textY           = 320
)

var fontFacePath = ""

// SetFontFacePath sets the font to do the business with
func SetFontFacePath(f string) {
	fontFacePath = f
}

// var sourceDir string

// func init() {
// 	// We need to set the source directory for the font
// 	_, filename, _, ok := runtime.Caller(0)
// 	if !ok {
// 		panic("No caller information")
// 	}
// 	sourceDir = path.Dir(filename)
// }

// ToDisk saves the image to disk
func ToDisk(initials, path string) {
	saveToDisk(initials, path, "", "")
}

// ToDiskCustom saves the image to disk
func ToDiskCustom(initials, path, bgColor, fontColor string) {
	saveToDisk(initials, path, bgColor, fontColor)
}

// saveToDisk saves the image to disk
func saveToDisk(initials, path, bgColor, fontColor string) {
	rgba, err := createAvatar(initials, bgColor, fontColor)
	if err != nil {
		log.Println(err)
		return
	}

	// Save image to disk
	out, err := os.Create(path)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
	defer out.Close()

	b := bufio.NewWriter(out)

	err = png.Encode(b, rgba)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	err = b.Flush()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
}

// ToHTTP sends the image to a http.ResponseWriter (as a PNG)
func ToHTTP(initials string, w http.ResponseWriter) {
	saveToHTTP(initials, "", "", w)
}

// ToHTTPCustom sends the image to a http.ResponseWriter (as a PNG)
func ToHTTPCustom(initials, bgColor, fontColor string, w http.ResponseWriter) {
	saveToHTTP(initials, bgColor, fontColor, w)
}

// saveToHTTP sends the image to a http.ResponseWriter (as a PNG)
func saveToHTTP(initials, bgColor, fontColor string, w http.ResponseWriter) {
	rgba, err := createAvatar(initials, bgColor, fontColor)
	if err != nil {
		log.Println(err)
		return
	}

	b := new(bytes.Buffer)
	key := fmt.Sprintf("avatar%s", initials) // for Etag

	err = png.Encode(b, rgba)
	if err != nil {
		log.Println("unable to encode image.")
	}

	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("Content-Length", strconv.Itoa(len(b.Bytes())))
	w.Header().Set("Cache-Control", "max-age=2592000") // 30 days
	w.Header().Set("Etag", `"`+key+`"`)

	if _, err := w.Write(b.Bytes()); err != nil {
		log.Println("unable to write image.")
	}
}

func cleanString(incoming string) string {
	incoming = strings.TrimSpace(incoming)

	// If its something like "firstname surname" get the initials out
	split := strings.Split(incoming, " ")
	if len(split) == 2 {
		incoming = split[0][0:1] + split[1][0:1]
	}

	// Max length of 2
	if len(incoming) > 2 {
		incoming = incoming[0:2]
	}

	// To upper and trimmed
	return strings.ToUpper(strings.TrimSpace(incoming))
}

func getFont(fontPath string) (*truetype.Font, error) {
	if fontPath == "" {
		fontPath = defaultfontFace
	}
	// Read the font data.
	fontBytes, err := ioutil.ReadFile(fontPath) //fmt.Sprintf("%s/%s", sourceDir, fontFaceName))
	if err != nil {
		return nil, err
	}

	return freetype.ParseFont(fontBytes)
}

var imageCache sync.Map

func getImage(initials string) *image.RGBA {
	value, ok := imageCache.Load(initials)

	if !ok {
		return nil
	}

	image, ok2 := value.(*image.RGBA)
	if !ok2 {
		return nil
	}
	return image
}

func setImage(initials string, image *image.RGBA) {
	imageCache.Store(initials, image)
}

func createAvatar(initials, bgColor, fontColor string) (*image.RGBA, error) {
	// Make sure the string is OK
	text := cleanString(initials)

	// Check cache
	cachedImage := getImage(text)
	if cachedImage != nil {
		return cachedImage, nil
	}

	// Load and get the font
	f, err := getFont(fontFacePath)
	if err != nil {
		return nil, err
	}

	// Setup the colors, text white, background based on first initial
	textColor := image.White
	if fontColor != "" {
		c, err := parseHexColorFast(fontColor)
		if err == nil {
			textColor = &image.Uniform{c}
		}
	}
	background := defaultColor(text[0:1])
	if bgColor != "" {
		c, err := parseHexColorFast(bgColor)
		if err == nil {
			background = image.Uniform{c}
		}
	}

	rgba := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))
	draw.Draw(rgba, rgba.Bounds(), &background, image.ZP, draw.Src)
	c := freetype.NewContext()
	c.SetDPI(dpi)
	c.SetFont(f)
	c.SetFontSize(fontSize)
	c.SetClip(rgba.Bounds())
	c.SetDst(rgba)
	c.SetSrc(textColor)
	c.SetHinting(font.HintingFull)

	// We need to convert the font into a "font.Face" so we can read the glyph
	// info
	to := truetype.Options{}
	to.Size = fontSize
	face := truetype.NewFace(f, &to)

	// Calculate the widths and print to image
	xPoints := []int{0, 0}
	textWidths := []int{0, 0}

	// Get the widths of the text characters
	for i, char := range text {
		width, ok := face.GlyphAdvance(rune(char))
		if !ok {
			return nil, err
		}

		textWidths[i] = int(float64(width) / 64)
	}

	// TODO need some tests for this
	if len(textWidths) == 1 {
		textWidths[1] = 0
	}

	// Get the combined width of the characters
	combinedWidth := textWidths[0] + spacer + textWidths[1]

	// Draw first character
	xPoints[0] = int((imageWidth - combinedWidth) / 2)
	xPoints[1] = int(xPoints[0] + textWidths[0] + spacer)

	for i, char := range text {
		pt := freetype.Pt(xPoints[i], textY)
		_, err := c.DrawString(string(char), pt)
		if err != nil {
			return nil, err
		}
	}

	// Cache it
	setImage(text, rgba)

	return rgba, nil
}
<|endoftext|>


<commit_before>package cgzip

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"hash/adler32"
	"hash/crc32"
	"hash/crc64"
	"io"
	"math/rand"
	"os/exec"
	"sync"
	"testing"
	"time"
)

type prettyTimer struct {
	name   string
	before time.Time
}

func newPrettyTimer(name string) *prettyTimer {
	return &prettyTimer{name, time.Now()}
}

func (pt *prettyTimer) stopAndPrintCompress(t *testing.T, size, processed int) {
	durationMs := int(int64(time.Now().Sub(pt.before)) / 1000)
	t.Log(pt.name + ":")
	t.Log("  size :", size)
	t.Log("  time :", durationMs, "ms")
	t.Log("  speed:", processed*1000/durationMs, "KB/s")
}

func (pt *prettyTimer) stopAndPrintUncompress(t *testing.T, processed int) {
	durationMs := int(int64(time.Now().Sub(pt.before)) / 1000)
	t.Log("     " + pt.name + ":")
	t.Log("       time :", durationMs, "ms")
	t.Log("       speed:", processed*1000/durationMs, "KB/s")
}

func compareCompressedBuffer(t *testing.T, source []byte, compressed *bytes.Buffer) {
	// compare using go's gunzip
	toGunzip := bytes.NewBuffer(compressed.Bytes())
	gunzip, err := gzip.NewReader(toGunzip)
	if err != nil {
		t.Errorf("gzip.NewReader failed: %v", err)
	}
	uncompressed := &bytes.Buffer{}
	pt := newPrettyTimer("go unzip")
	_, err = io.Copy(uncompressed, gunzip)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	pt.stopAndPrintUncompress(t, uncompressed.Len())
	if !bytes.Equal(source, uncompressed.Bytes()) {
		t.Errorf("Bytes are not equal")
	}

	// compare using cgzip gunzip
	toGunzip = bytes.NewBuffer(compressed.Bytes())
	cgunzip, err := NewReader(toGunzip)
	if err != nil {
		t.Errorf("cgzip.NewReader failed: %v", err)
	}
	uncompressed = &bytes.Buffer{}
	pt = newPrettyTimer("cgzip unzip")
	_, err = io.Copy(uncompressed, cgunzip)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	pt.stopAndPrintUncompress(t, uncompressed.Len())
	if !bytes.Equal(source, uncompressed.Bytes()) {
		t.Errorf("Bytes are not equal")
	}
}

func testChecksums(t *testing.T, data []byte) {
	t.Log("Checksums:")

	// crc64 with go library
	goCrc64 := crc64.New(crc64.MakeTable(crc64.ECMA))
	toChecksum := bytes.NewBuffer(data)
	pt := newPrettyTimer("go crc64")
	_, err := io.Copy(goCrc64, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	pt.stopAndPrintUncompress(t, len(data))

	// adler32 with go library
	goAdler32 := adler32.New()
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("go adler32")
	_, err = io.Copy(goAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	goResult := goAdler32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", goResult)

	// adler32 with cgzip library
	cgzipAdler32 := NewAdler32()
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("cgzip adler32")
	_, err = io.Copy(cgzipAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	cgzipResult := cgzipAdler32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", cgzipResult)

	// test both results are the same
	if goResult != cgzipResult {
		t.Errorf("go and cgzip adler32 mismatch")
	}

	// now test partial checksuming also works with adler32
	cutoff := len(data) / 3
	toChecksum = bytes.NewBuffer(data[0:cutoff])
	cgzipAdler32.Reset()
	_, err = io.Copy(cgzipAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	adler1 := cgzipAdler32.Sum32()
	t.Log("   a1   :", adler1)
	t.Log("   len1 :", cutoff)

	toChecksum = bytes.NewBuffer(data[cutoff:])
	cgzipAdler32.Reset()
	_, err = io.Copy(cgzipAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	adler2 := cgzipAdler32.Sum32()
	t.Log("   a2   :", adler2)
	t.Log("   len2 :", len(data)-cutoff)

	adlerCombined := Adler32Combine(adler1, adler2, len(data)-cutoff)
	t.Log("   comb :", adlerCombined)

	if cgzipResult != adlerCombined {
		t.Errorf("full and combined adler32 mismatch")
	}

	// crc32 with go library
	goCrc32 := crc32.New(crc32.MakeTable(crc32.IEEE))
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("go crc32")
	_, err = io.Copy(goCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	goResult = goCrc32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", goResult)

	// crc32 with cgzip library
	cgzipCrc32 := NewCrc32()
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("cgzip crc32")
	_, err = io.Copy(cgzipCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	cgzipResult = cgzipCrc32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", cgzipResult)

	// test both results are the same
	if goResult != cgzipResult {
		t.Errorf("go and cgzip crc32 mismatch")
	}

	// now test partial checksuming also works with crc32
	toChecksum = bytes.NewBuffer(data[0:cutoff])
	cgzipCrc32.Reset()
	_, err = io.Copy(cgzipCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	crc1 := cgzipCrc32.Sum32()
	t.Log("   crc1 :", crc1)
	t.Log("   len1 :", cutoff)

	toChecksum = bytes.NewBuffer(data[cutoff:])
	cgzipCrc32.Reset()
	_, err = io.Copy(cgzipCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	crc2 := cgzipCrc32.Sum32()
	t.Log("   crc2 :", crc2)
	t.Log("   len2 :", len(data)-cutoff)

	crcCombined := Crc32Combine(crc1, crc2, len(data)-cutoff)
	t.Log("   comb :", crcCombined)

	if cgzipResult != crcCombined {
		t.Errorf("full and combined crc32 mismatch")
	}
}

func runCompare(t *testing.T, testSize int, level int) {

	// create a test chunk, put semi-random bytes in there
	// (so compression actually will compress some)
	toEncode := make([]byte, testSize)
	where := 0
	for where < testSize {
		toFill := rand.Intn(16)
		filler := 0x61 + rand.Intn(24)
		for i := 0; i < toFill && where < testSize; i++ {
			toEncode[where] = byte(filler)
			where++
		}
	}
	t.Log("Original size:", len(toEncode))

	// now time a regular gzip writer to a Buffer
	compressed := &bytes.Buffer{}
	reader := bytes.NewBuffer(toEncode)
	pt := newPrettyTimer("Go gzip")
	gz, err := gzip.NewWriterLevel(compressed, level)
	_, err = io.Copy(gz, reader)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	gz.Close()
	pt.stopAndPrintCompress(t, compressed.Len(), len(toEncode))
	compareCompressedBuffer(t, toEncode, compressed)

	// now time a forked gzip
	compressed2 := &bytes.Buffer{}
	reader = bytes.NewBuffer(toEncode)
	cmd := exec.Command("gzip", fmt.Sprintf("-%v", level), "-c")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		t.Errorf("StdoutPipe failed: %v", err)
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		t.Errorf("StdinPipe failed: %v", err)
	}
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		io.Copy(compressed2, stdout)
		wg.Done()
	}()
	if err = cmd.Start(); err != nil {
		t.Errorf("Start failed: %v", err)
	}
	pt = newPrettyTimer("Forked gzip")
	_, err = io.Copy(stdin, reader)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	stdin.Close()
	wg.Wait()
	if err := cmd.Wait(); err != nil {
		t.Errorf("Wait failed: %v", err)
	}
	pt.stopAndPrintCompress(t, compressed2.Len(), len(toEncode))
	compareCompressedBuffer(t, toEncode, compressed2)

	// and time the cgo version
	compressed3 := &bytes.Buffer{}
	reader = bytes.NewBuffer(toEncode)
	pt = newPrettyTimer("cgzip")
	cgz, err := NewWriterLevel(compressed3, level)
	if err != nil {
		t.Errorf("NewWriterLevel failed: %v", err)
	}
	_, err = io.Copy(cgz, reader)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	if err := cgz.Flush(); err != nil {
		t.Errorf("Flush failed: %v", err)
	}
	if err := cgz.Close(); err != nil {
		t.Errorf("Close failed: %v", err)
	}
	pt.stopAndPrintCompress(t, compressed3.Len(), len(toEncode))
	compareCompressedBuffer(t, toEncode, compressed3)

	testChecksums(t, toEncode)
}

// use 'go test -v' and bigger sizes to show meaningful rates
func TestCompare(t *testing.T) {
	runCompare(t, 1*1024*1024, 1)
}

func TestCompareBest(t *testing.T) {
	runCompare(t, 1*1024*1024, 9)
}
<commit_msg>Skip long tests in "go test -short" mode.<commit_after>package cgzip

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"hash/adler32"
	"hash/crc32"
	"hash/crc64"
	"io"
	"math/rand"
	"os/exec"
	"sync"
	"testing"
	"time"
)

type prettyTimer struct {
	name   string
	before time.Time
}

func newPrettyTimer(name string) *prettyTimer {
	return &prettyTimer{name, time.Now()}
}

func (pt *prettyTimer) stopAndPrintCompress(t *testing.T, size, processed int) {
	durationMs := int(int64(time.Now().Sub(pt.before)) / 1000)
	t.Log(pt.name + ":")
	t.Log("  size :", size)
	t.Log("  time :", durationMs, "ms")
	t.Log("  speed:", processed*1000/durationMs, "KB/s")
}

func (pt *prettyTimer) stopAndPrintUncompress(t *testing.T, processed int) {
	durationMs := int(int64(time.Now().Sub(pt.before)) / 1000)
	t.Log("     " + pt.name + ":")
	t.Log("       time :", durationMs, "ms")
	t.Log("       speed:", processed*1000/durationMs, "KB/s")
}

func compareCompressedBuffer(t *testing.T, source []byte, compressed *bytes.Buffer) {
	// compare using go's gunzip
	toGunzip := bytes.NewBuffer(compressed.Bytes())
	gunzip, err := gzip.NewReader(toGunzip)
	if err != nil {
		t.Errorf("gzip.NewReader failed: %v", err)
	}
	uncompressed := &bytes.Buffer{}
	pt := newPrettyTimer("go unzip")
	_, err = io.Copy(uncompressed, gunzip)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	pt.stopAndPrintUncompress(t, uncompressed.Len())
	if !bytes.Equal(source, uncompressed.Bytes()) {
		t.Errorf("Bytes are not equal")
	}

	// compare using cgzip gunzip
	toGunzip = bytes.NewBuffer(compressed.Bytes())
	cgunzip, err := NewReader(toGunzip)
	if err != nil {
		t.Errorf("cgzip.NewReader failed: %v", err)
	}
	uncompressed = &bytes.Buffer{}
	pt = newPrettyTimer("cgzip unzip")
	_, err = io.Copy(uncompressed, cgunzip)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	pt.stopAndPrintUncompress(t, uncompressed.Len())
	if !bytes.Equal(source, uncompressed.Bytes()) {
		t.Errorf("Bytes are not equal")
	}
}

func testChecksums(t *testing.T, data []byte) {
	t.Log("Checksums:")

	// crc64 with go library
	goCrc64 := crc64.New(crc64.MakeTable(crc64.ECMA))
	toChecksum := bytes.NewBuffer(data)
	pt := newPrettyTimer("go crc64")
	_, err := io.Copy(goCrc64, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	pt.stopAndPrintUncompress(t, len(data))

	// adler32 with go library
	goAdler32 := adler32.New()
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("go adler32")
	_, err = io.Copy(goAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	goResult := goAdler32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", goResult)

	// adler32 with cgzip library
	cgzipAdler32 := NewAdler32()
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("cgzip adler32")
	_, err = io.Copy(cgzipAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	cgzipResult := cgzipAdler32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", cgzipResult)

	// test both results are the same
	if goResult != cgzipResult {
		t.Errorf("go and cgzip adler32 mismatch")
	}

	// now test partial checksuming also works with adler32
	cutoff := len(data) / 3
	toChecksum = bytes.NewBuffer(data[0:cutoff])
	cgzipAdler32.Reset()
	_, err = io.Copy(cgzipAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	adler1 := cgzipAdler32.Sum32()
	t.Log("   a1   :", adler1)
	t.Log("   len1 :", cutoff)

	toChecksum = bytes.NewBuffer(data[cutoff:])
	cgzipAdler32.Reset()
	_, err = io.Copy(cgzipAdler32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	adler2 := cgzipAdler32.Sum32()
	t.Log("   a2   :", adler2)
	t.Log("   len2 :", len(data)-cutoff)

	adlerCombined := Adler32Combine(adler1, adler2, len(data)-cutoff)
	t.Log("   comb :", adlerCombined)

	if cgzipResult != adlerCombined {
		t.Errorf("full and combined adler32 mismatch")
	}

	// crc32 with go library
	goCrc32 := crc32.New(crc32.MakeTable(crc32.IEEE))
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("go crc32")
	_, err = io.Copy(goCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	goResult = goCrc32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", goResult)

	// crc32 with cgzip library
	cgzipCrc32 := NewCrc32()
	toChecksum = bytes.NewBuffer(data)
	pt = newPrettyTimer("cgzip crc32")
	_, err = io.Copy(cgzipCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	cgzipResult = cgzipCrc32.Sum32()
	pt.stopAndPrintUncompress(t, len(data))
	t.Log("       sum  :", cgzipResult)

	// test both results are the same
	if goResult != cgzipResult {
		t.Errorf("go and cgzip crc32 mismatch")
	}

	// now test partial checksuming also works with crc32
	toChecksum = bytes.NewBuffer(data[0:cutoff])
	cgzipCrc32.Reset()
	_, err = io.Copy(cgzipCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	crc1 := cgzipCrc32.Sum32()
	t.Log("   crc1 :", crc1)
	t.Log("   len1 :", cutoff)

	toChecksum = bytes.NewBuffer(data[cutoff:])
	cgzipCrc32.Reset()
	_, err = io.Copy(cgzipCrc32, toChecksum)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	crc2 := cgzipCrc32.Sum32()
	t.Log("   crc2 :", crc2)
	t.Log("   len2 :", len(data)-cutoff)

	crcCombined := Crc32Combine(crc1, crc2, len(data)-cutoff)
	t.Log("   comb :", crcCombined)

	if cgzipResult != crcCombined {
		t.Errorf("full and combined crc32 mismatch")
	}
}

func runCompare(t *testing.T, testSize int, level int) {

	// create a test chunk, put semi-random bytes in there
	// (so compression actually will compress some)
	toEncode := make([]byte, testSize)
	where := 0
	for where < testSize {
		toFill := rand.Intn(16)
		filler := 0x61 + rand.Intn(24)
		for i := 0; i < toFill && where < testSize; i++ {
			toEncode[where] = byte(filler)
			where++
		}
	}
	t.Log("Original size:", len(toEncode))

	// now time a regular gzip writer to a Buffer
	compressed := &bytes.Buffer{}
	reader := bytes.NewBuffer(toEncode)
	pt := newPrettyTimer("Go gzip")
	gz, err := gzip.NewWriterLevel(compressed, level)
	_, err = io.Copy(gz, reader)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	gz.Close()
	pt.stopAndPrintCompress(t, compressed.Len(), len(toEncode))
	compareCompressedBuffer(t, toEncode, compressed)

	// now time a forked gzip
	compressed2 := &bytes.Buffer{}
	reader = bytes.NewBuffer(toEncode)
	cmd := exec.Command("gzip", fmt.Sprintf("-%v", level), "-c")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		t.Errorf("StdoutPipe failed: %v", err)
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		t.Errorf("StdinPipe failed: %v", err)
	}
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		io.Copy(compressed2, stdout)
		wg.Done()
	}()
	if err = cmd.Start(); err != nil {
		t.Errorf("Start failed: %v", err)
	}
	pt = newPrettyTimer("Forked gzip")
	_, err = io.Copy(stdin, reader)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	stdin.Close()
	wg.Wait()
	if err := cmd.Wait(); err != nil {
		t.Errorf("Wait failed: %v", err)
	}
	pt.stopAndPrintCompress(t, compressed2.Len(), len(toEncode))
	compareCompressedBuffer(t, toEncode, compressed2)

	// and time the cgo version
	compressed3 := &bytes.Buffer{}
	reader = bytes.NewBuffer(toEncode)
	pt = newPrettyTimer("cgzip")
	cgz, err := NewWriterLevel(compressed3, level)
	if err != nil {
		t.Errorf("NewWriterLevel failed: %v", err)
	}
	_, err = io.Copy(cgz, reader)
	if err != nil {
		t.Errorf("Copy failed: %v", err)
	}
	if err := cgz.Flush(); err != nil {
		t.Errorf("Flush failed: %v", err)
	}
	if err := cgz.Close(); err != nil {
		t.Errorf("Close failed: %v", err)
	}
	pt.stopAndPrintCompress(t, compressed3.Len(), len(toEncode))
	compareCompressedBuffer(t, toEncode, compressed3)

	testChecksums(t, toEncode)
}

// use 'go test -v' and bigger sizes to show meaningful rates
func TestCompare(t *testing.T) {
	testSize := 1 * 1024 * 1024
	if testing.Short() {
		testSize /= 10
	}
	runCompare(t, testSize, 1)
}

func TestCompareBest(t *testing.T) {
	testSize := 1 * 1024 * 1024
	if testing.Short() {
		testSize /= 10
	}
	runCompare(t, testSize, 9)
}
<|endoftext|>


<commit_before>package endly

import (
	"fmt"
	"github.com/viant/toolbox"
	"strings"
	"time"
)

//ProcessServiceID represents a system process service id
const ProcessServiceID = "process"

//ProcessServiceStartAction represents a process start action
const ProcessServiceStartAction = "start"

//ProcessServiceStatusAction represents a process status check
const ProcessServiceStatusAction = "status"

//ProcessServiceStopAction represents stop action
const ProcessServiceStopAction = "stop"

//ProcessServiceStopAllAction represents stop-all action
const ProcessServiceStopAllAction = "stop-all"

type processService struct {
	*AbstractService
}

func (s *processService) Run(context *Context, request interface{}) *ServiceResponse {
	startEvent := s.Begin(context, request, Pairs("request", request))
	var response = &ServiceResponse{Status: "ok"}
	defer s.End(context)(startEvent, Pairs("response", response))
	var err error
	switch actualRequest := request.(type) {
	case *ProcessStartRequest:
		response.Response, err = s.startProcess(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to start process: %v, %v", actualRequest.Command, err)
		}
	case *ProcessStopRequest:
		response.Response, err = s.stopProcess(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to stop process: %v, %v", actualRequest.Pid, err)
		}
	case *ProcessStopAllRequest:
		response.Response, err = s.stopAllProcesses(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to stop process: %v, %v", actualRequest.Input, err)
		}

	case *ProcessStatusRequest:
		response.Response, err = s.checkProcess(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to stop process: %v, %v", actualRequest, err)
		}

	default:
		response.Error = fmt.Sprintf("unsupported request type: %T", request)

	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

func (s *processService) stopAllProcesses(context *Context, request *ProcessStopAllRequest) (*CommandResponse, error) {
	status, err := s.checkProcess(context, &ProcessStatusRequest{
		Target:  request.Target,
		Command: request.Input,
	})
	if err != nil {
		return nil, err
	}

	var respose *CommandResponse
	for _, info := range status.Processes {
		respose, err = s.stopProcess(context, &ProcessStopRequest{
			Target: request.Target,
			Pid:    info.Pid,
		})
		if err != nil {
			return nil, err
		}
	}
	return respose, nil
}

func (s *processService) checkProcess(context *Context, request *ProcessStatusRequest) (*ProcessStatusResponse, error) {
	var response = &ProcessStatusResponse{
		Processes: make([]*ProcessInfo, 0),
	}

	command := fmt.Sprintf("ps -ef | grep %v", request.Command)
	if strings.Contains(request.Command, " ") {
		command = fmt.Sprintf("ps -ef | grep '%v'", request.Command)
	}
	commandResponse, err := context.Execute(request.Target, &ExtractableCommand{
		Executions: []*Execution{
			{
				Command: command,
			},
		},
	})
	if err != nil {
		return nil, err
	}

	for _, line := range strings.Split(commandResponse.Stdout(), "\r\n") {
		if strings.Contains(line, "grep") {
			continue
		}
		line = strings.TrimSpace(line)
		columns, ok := ExtractColumns(line)
		if len(columns) < 3 || !ok {
			continue
		}
		info := &ProcessInfo{
			Pid:       toolbox.AsInt(columns[1]),
			Command:   request.Command,
			Arguments: make([]string, 0),
			Stdin:     command,
			Stdout:    line,
		}
		var expectArgument = false
		for _, column := range columns {
			if expectArgument {
				info.Arguments = append(info.Arguments, column)
				continue
			}
			if strings.Contains(column, request.Command) {
				info.Name = column
				expectArgument = true
			}
		}
		info.Stdout = strings.Join(columns, " ")
		response.Processes = append(response.Processes, info)
	}
	if len(response.Processes) > 0 {
		response.Pid = response.Processes[0].Pid
	}
	return response, nil
}

func (s *processService) stopProcess(context *Context, request *ProcessStopRequest) (*CommandResponse, error) {
	commandResult, err := context.ExecuteAsSuperUser(request.Target, &ExtractableCommand{
		Executions: []*Execution{
			{
				Command: fmt.Sprintf("kill -9 %v", request.Pid),
			},
		},
	})
	if err != nil {
		return nil, err
	}
	return commandResult, err
}

func indexProcesses(processes ...*ProcessInfo) map[int]*ProcessInfo {
	var result = make(map[int]*ProcessInfo)
	for _, process := range processes {
		result[process.Pid] = process
	}
	return result
}

func (s *processService) startProcess(context *Context, request *ProcessStartRequest) (*ProcessStartResponse, error) {
	origProcesses, err := s.checkProcess(context, &ProcessStatusRequest{
		Target:  request.Target,
		Command: request.Command,
	})

	var result = &ProcessStartResponse{}
	if err != nil {
		return nil, err
	}
	for _, process := range origProcesses.Processes {
		if strings.Join(process.Arguments, " ") == strings.Join(request.Arguments, " ") {
			_, err := s.stopProcess(context, &ProcessStopRequest{
				Pid:    process.Pid,
				Target: request.Target,
			})
			if err != nil {
				return nil, err
			}
		}
	}
	changeDirCommand := fmt.Sprintf("cd %v ", request.Directory)

	var startCommand = request.Command + " " + strings.Join(request.Arguments, " ") + " &"
	if request.ImmuneToHangups {
		startCommand = fmt.Sprintf("nohup  %v", startCommand)
	}
	_, err = context.Execute(request.Target, &ExtractableCommand{
		Options: request.Options,
		Executions: []*Execution{
			{
				Command: changeDirCommand,
			},
			{
				Command: startCommand,
			},
		},
	})
	if err != nil {
		return nil, err
	}
	time.Sleep(time.Second)
	newProcesses, err := s.checkProcess(context, &ProcessStatusRequest{
		Target:  request.Target,
		Command: request.Command,
	})
	if err != nil {
		return nil, err
	}

	result.Info = make([]*ProcessInfo, 0)
	existingProcesses := indexProcesses(origProcesses.Processes...)

	for _, candidate := range newProcesses.Processes {
		if _, has := existingProcesses[candidate.Pid]; !has {
			result.Info = append(result.Info, candidate)
			break
		}
	}
	return result, nil
}

func (s *processService) NewRequest(action string) (interface{}, error) {
	switch action {
	case ProcessServiceStartAction:
		return &ProcessStartRequest{}, nil
	case ProcessServiceStatusAction:
		return &ProcessStatusRequest{}, nil
	case ProcessServiceStopAction:
		return &ProcessStopRequest{}, nil
	case ProcessServiceStopAllAction:
		return &ProcessStopAllRequest{}, nil

	}
	return s.AbstractService.NewRequest(action)
}

//NewProcessService returns a new system process service.
func NewProcessService() Service {
	var result = &processService{
		AbstractService: NewAbstractService(ProcessServiceID,
			ProcessServiceStartAction,
			ProcessServiceStatusAction,
			ProcessServiceStopAction,
			ProcessServiceStopAllAction),
	}
	result.AbstractService.Service = result
	return result
}
<commit_msg>patched process service to avoid quoting pipe commands<commit_after>package endly

import (
	"fmt"
	"github.com/viant/toolbox"
	"strings"
	"time"
)

//ProcessServiceID represents a system process service id
const ProcessServiceID = "process"

//ProcessServiceStartAction represents a process start action
const ProcessServiceStartAction = "start"

//ProcessServiceStatusAction represents a process status check
const ProcessServiceStatusAction = "status"

//ProcessServiceStopAction represents stop action
const ProcessServiceStopAction = "stop"

//ProcessServiceStopAllAction represents stop-all action
const ProcessServiceStopAllAction = "stop-all"

type processService struct {
	*AbstractService
}

func (s *processService) Run(context *Context, request interface{}) *ServiceResponse {
	startEvent := s.Begin(context, request, Pairs("request", request))
	var response = &ServiceResponse{Status: "ok"}
	defer s.End(context)(startEvent, Pairs("response", response))
	var err error
	switch actualRequest := request.(type) {
	case *ProcessStartRequest:
		response.Response, err = s.startProcess(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to start process: %v, %v", actualRequest.Command, err)
		}
	case *ProcessStopRequest:
		response.Response, err = s.stopProcess(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to stop process: %v, %v", actualRequest.Pid, err)
		}
	case *ProcessStopAllRequest:
		response.Response, err = s.stopAllProcesses(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to stop process: %v, %v", actualRequest.Input, err)
		}

	case *ProcessStatusRequest:
		response.Response, err = s.checkProcess(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("failed to stop process: %v, %v", actualRequest, err)
		}

	default:
		response.Error = fmt.Sprintf("unsupported request type: %T", request)

	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

func (s *processService) stopAllProcesses(context *Context, request *ProcessStopAllRequest) (*CommandResponse, error) {
	status, err := s.checkProcess(context, &ProcessStatusRequest{
		Target:  request.Target,
		Command: request.Input,
	})
	if err != nil {
		return nil, err
	}

	var respose *CommandResponse
	for _, info := range status.Processes {
		respose, err = s.stopProcess(context, &ProcessStopRequest{
			Target: request.Target,
			Pid:    info.Pid,
		})
		if err != nil {
			return nil, err
		}
	}
	return respose, nil
}

func (s *processService) checkProcess(context *Context, request *ProcessStatusRequest) (*ProcessStatusResponse, error) {
	var response = &ProcessStatusResponse{
		Processes: make([]*ProcessInfo, 0),
	}

	command := fmt.Sprintf("ps -ef | grep %v", request.Command)
	if strings.Contains(request.Command, " ") &&  ! strings.Contains(request.Command, "|") {
		command = fmt.Sprintf("ps -ef | grep '%v'", request.Command)
	}
	commandResponse, err := context.Execute(request.Target, &ExtractableCommand{
		Executions: []*Execution{
			{
				Command: command,
			},
		},
	})
	if err != nil {
		return nil, err
	}

	for _, line := range strings.Split(commandResponse.Stdout(), "\r\n") {
		if strings.Contains(line, "grep") {
			continue
		}
		line = strings.TrimSpace(line)
		columns, ok := ExtractColumns(line)
		if len(columns) < 3 || !ok {
			continue
		}
		info := &ProcessInfo{
			Pid:       toolbox.AsInt(columns[1]),
			Command:   request.Command,
			Arguments: make([]string, 0),
			Stdin:     command,
			Stdout:    line,
		}
		var expectArgument = false
		for _, column := range columns {
			if expectArgument {
				info.Arguments = append(info.Arguments, column)
				continue
			}
			if strings.Contains(column, request.Command) {
				info.Name = column
				expectArgument = true
			}
		}
		info.Stdout = strings.Join(columns, " ")
		response.Processes = append(response.Processes, info)
	}
	if len(response.Processes) > 0 {
		response.Pid = response.Processes[0].Pid
	}
	return response, nil
}

func (s *processService) stopProcess(context *Context, request *ProcessStopRequest) (*CommandResponse, error) {
	commandResult, err := context.ExecuteAsSuperUser(request.Target, &ExtractableCommand{
		Executions: []*Execution{
			{
				Command: fmt.Sprintf("kill -9 %v", request.Pid),
			},
		},
	})
	if err != nil {
		return nil, err
	}
	return commandResult, err
}

func indexProcesses(processes ...*ProcessInfo) map[int]*ProcessInfo {
	var result = make(map[int]*ProcessInfo)
	for _, process := range processes {
		result[process.Pid] = process
	}
	return result
}

func (s *processService) startProcess(context *Context, request *ProcessStartRequest) (*ProcessStartResponse, error) {
	origProcesses, err := s.checkProcess(context, &ProcessStatusRequest{
		Target:  request.Target,
		Command: request.Command,
	})

	var result = &ProcessStartResponse{}
	if err != nil {
		return nil, err
	}
	for _, process := range origProcesses.Processes {
		if strings.Join(process.Arguments, " ") == strings.Join(request.Arguments, " ") {
			_, err := s.stopProcess(context, &ProcessStopRequest{
				Pid:    process.Pid,
				Target: request.Target,
			})
			if err != nil {
				return nil, err
			}
		}
	}
	changeDirCommand := fmt.Sprintf("cd %v ", request.Directory)

	var startCommand = request.Command + " " + strings.Join(request.Arguments, " ") + " &"
	if request.ImmuneToHangups {
		startCommand = fmt.Sprintf("nohup  %v", startCommand)
	}
	_, err = context.Execute(request.Target, &ExtractableCommand{
		Options: request.Options,
		Executions: []*Execution{
			{
				Command: changeDirCommand,
			},
			{
				Command: startCommand,
			},
		},
	})
	if err != nil {
		return nil, err
	}
	time.Sleep(time.Second)
	newProcesses, err := s.checkProcess(context, &ProcessStatusRequest{
		Target:  request.Target,
		Command: request.Command,
	})
	if err != nil {
		return nil, err
	}

	result.Info = make([]*ProcessInfo, 0)
	existingProcesses := indexProcesses(origProcesses.Processes...)

	for _, candidate := range newProcesses.Processes {
		if _, has := existingProcesses[candidate.Pid]; !has {
			result.Info = append(result.Info, candidate)
			break
		}
	}
	return result, nil
}

func (s *processService) NewRequest(action string) (interface{}, error) {
	switch action {
	case ProcessServiceStartAction:
		return &ProcessStartRequest{}, nil
	case ProcessServiceStatusAction:
		return &ProcessStatusRequest{}, nil
	case ProcessServiceStopAction:
		return &ProcessStopRequest{}, nil
	case ProcessServiceStopAllAction:
		return &ProcessStopAllRequest{}, nil

	}
	return s.AbstractService.NewRequest(action)
}

//NewProcessService returns a new system process service.
func NewProcessService() Service {
	var result = &processService{
		AbstractService: NewAbstractService(ProcessServiceID,
			ProcessServiceStartAction,
			ProcessServiceStatusAction,
			ProcessServiceStopAction,
			ProcessServiceStopAllAction),
	}
	result.AbstractService.Service = result
	return result
}
<|endoftext|>


<commit_before>package gonvim

import (
	"sync"

	"github.com/dzhou121/ui"
)

// Finder is a fuzzy finder window
type Finder struct {
	box         *ui.Box
	pattern     *SpanHandler
	patternText string
	items       []*FinderItem
	mutex       *sync.Mutex
	width       int
	cursor      *ui.Area
}

// FinderItem is the result shown
type FinderItem struct {
	item *SpanHandler
}

func initFinder() *Finder {
	width := 600

	box := ui.NewHorizontalBox()
	box.SetSize(width, 500)

	patternHandler := &SpanHandler{}
	pattern := ui.NewArea(patternHandler)
	patternHandler.span = pattern
	patternHandler.paddingLeft = 10
	patternHandler.paddingRight = 10
	patternHandler.paddingTop = 8
	patternHandler.paddingBottom = 8

	cursor := ui.NewArea(&AreaHandler{})
	cursor.SetSize(1, 24)
	cursor.SetBackground(&ui.Brush{
		Type: ui.Solid,
		R:    1,
		G:    1,
		B:    1,
		A:    0.9,
	})

	box.Append(pattern, false)
	box.Append(cursor, false)
	box.SetShadow(0, 2, 0, 0, 0, 1, 4)
	box.Hide()

	f := &Finder{
		box:     box,
		pattern: patternHandler,
		items:   []*FinderItem{},
		mutex:   &sync.Mutex{},
		width:   width,
		cursor:  cursor,
	}
	return f
}

func (f *Finder) show() {
	ui.QueueMain(func() {
		f.box.Show()
	})
}

func (f *Finder) hide() {
	ui.QueueMain(func() {
		f.box.Hide()
	})
}

func (f *Finder) cursorPos(args []interface{}) {
	p := reflectToInt(args[0])
	x := p*editor.font.width + f.pattern.paddingLeft
	ui.QueueMain(func() {
		f.cursor.SetPosition(x, 0)
	})
}

func (f *Finder) selectResult(args []interface{}) {
	selected := reflectToInt(args[0])
	for i := 0; i < len(f.items); i++ {
		item := f.items[i]
		if selected == i {
			item.item.SetBackground(newRGBA(81, 154, 186, 0.5))
			ui.QueueMain(func() {
				item.item.span.QueueRedrawAll()
			})
		} else {
			item.item.SetBackground(newRGBA(14, 17, 18, 1))
			ui.QueueMain(func() {
				item.item.span.QueueRedrawAll()
			})
		}
	}
}

func (f *Finder) showPattern(args []interface{}) {
	p := args[0].(string)
	f.pattern.span.SetSize(f.width, 8+8+editor.font.height)
	f.pattern.SetText(p)
	f.patternText = p
	f.pattern.SetFont(editor.font.font)
	fg := newRGBA(205, 211, 222, 1)
	f.pattern.SetColor(fg)
	f.pattern.SetBackground(newRGBA(14, 17, 18, 1))
	ui.QueueMain(func() {
		f.box.Show()
		f.pattern.span.Show()
		f.pattern.span.QueueRedrawAll()
	})
}

func (f *Finder) rePosition() {
	x := (editor.width - f.width) / 2
	ui.QueueMain(func() {
		f.box.SetPosition(x, 0)
	})
}

func (f *Finder) showResult(args []interface{}) {
	f.mutex.Lock()
	defer f.mutex.Unlock()
	result := args[0].([]interface{})
	selected := reflectToInt(args[1])
	match := [][]int{}
	for _, i := range args[2].([]interface{}) {
		m := []int{}
		for _, n := range i.([]interface{}) {
			m = append(m, reflectToInt(n))
		}
		match = append(match, m)
	}
	for i, item := range result {
		if i > len(f.items)-1 {
			height := 8 + 8 + editor.font.height
			width := f.width

			itemHandler := &SpanHandler{}
			itemSpan := ui.NewArea(itemHandler)
			itemHandler.span = itemSpan
			itemHandler.matchColor = newRGBA(81, 154, 186, 1)
			y := height * (i + 1)
			ui.QueueMain(func() {
				f.box.Append(itemSpan, false)
				itemSpan.SetSize(width, height)
				itemSpan.SetPosition(0, y)
			})

			f.items = append(f.items, &FinderItem{
				item: itemHandler,
			})
		}
		itemHandler := f.items[i]
		itemHandler.item.SetText(item.(string))
		itemHandler.item.SetFont(editor.font.font)
		itemHandler.item.paddingLeft = 10
		itemHandler.item.paddingRight = 10
		itemHandler.item.paddingTop = 8
		itemHandler.item.paddingBottom = 8
		fg := newRGBA(205, 211, 222, 1)
		itemHandler.item.SetColor(fg)
		itemHandler.item.match = f.patternText
		itemHandler.item.matchIndex = match[i]
		if i == selected {
			itemHandler.item.SetBackground(newRGBA(81, 154, 186, 0.5))
		} else {
			itemHandler.item.SetBackground(newRGBA(14, 17, 18, 1))
		}
		ui.QueueMain(func() {
			itemHandler.item.span.Show()
			itemHandler.item.span.QueueRedrawAll()
		})
	}
	for i := len(result); i < len(f.items); i++ {
		item := f.items[i]
		ui.QueueMain(func() {
			item.item.span.Hide()
		})
	}
}
<commit_msg>finder complete<commit_after>package gonvim

import (
	"sync"

	"github.com/dzhou121/ui"
)

// Finder is a fuzzy finder window
type Finder struct {
	box         *ui.Box
	pattern     *SpanHandler
	patternText string
	items       []*FinderItem
	mutex       *sync.Mutex
	width       int
	cursor      *ui.Area
}

// FinderItem is the result shown
type FinderItem struct {
	item *SpanHandler
}

func initFinder() *Finder {
	width := 600

	box := ui.NewHorizontalBox()
	box.SetSize(width, 500)

	patternHandler := &SpanHandler{}
	pattern := ui.NewArea(patternHandler)
	patternHandler.span = pattern
	patternHandler.paddingLeft = 10
	patternHandler.paddingRight = 10
	patternHandler.paddingTop = 8
	patternHandler.paddingBottom = 8

	cursor := ui.NewArea(&AreaHandler{})
	cursor.SetSize(1, 24)
	cursor.SetBackground(&ui.Brush{
		Type: ui.Solid,
		R:    1,
		G:    1,
		B:    1,
		A:    0.9,
	})

	box.Append(pattern, false)
	box.Append(cursor, false)
	box.SetShadow(0, 2, 0, 0, 0, 1, 4)
	box.Hide()

	f := &Finder{
		box:     box,
		pattern: patternHandler,
		items:   []*FinderItem{},
		mutex:   &sync.Mutex{},
		width:   width,
		cursor:  cursor,
	}
	return f
}

func (f *Finder) show() {
	ui.QueueMain(func() {
		f.box.Show()
	})
}

func (f *Finder) hide() {
	ui.QueueMain(func() {
		f.box.Hide()
	})
}

func (f *Finder) cursorPos(args []interface{}) {
	f.cursor.SetSize(1, editor.font.lineHeight)
	p := reflectToInt(args[0])
	x := p*editor.font.width + f.pattern.paddingLeft
	ui.QueueMain(func() {
		f.cursor.SetPosition(x, f.pattern.paddingTop/2)
	})
}

func (f *Finder) selectResult(args []interface{}) {
	selected := reflectToInt(args[0])
	for i := 0; i < len(f.items); i++ {
		item := f.items[i]
		if selected == i {
			item.item.SetBackground(newRGBA(81, 154, 186, 1))
			ui.QueueMain(func() {
				item.item.span.QueueRedrawAll()
			})
		} else {
			item.item.SetBackground(newRGBA(14, 17, 18, 1))
			ui.QueueMain(func() {
				item.item.span.QueueRedrawAll()
			})
		}
	}
}

func (f *Finder) showPattern(args []interface{}) {
	p := args[0].(string)
	f.pattern.span.SetSize(f.width, 8+8+editor.font.height)
	f.pattern.SetText(p)
	f.patternText = p
	f.pattern.SetFont(editor.font.font)
	fg := newRGBA(205, 211, 222, 1)
	f.pattern.SetColor(fg)
	f.pattern.SetBackground(newRGBA(14, 17, 18, 1))
	ui.QueueMain(func() {
		f.box.Show()
		f.pattern.span.Show()
		f.pattern.span.QueueRedrawAll()
	})
}

func (f *Finder) rePosition() {
	x := (editor.width - f.width) / 2
	ui.QueueMain(func() {
		f.box.SetPosition(x, 0)
	})
}

func (f *Finder) showResult(args []interface{}) {
	f.mutex.Lock()
	defer f.mutex.Unlock()
	result := args[0].([]interface{})
	selected := reflectToInt(args[1])
	match := [][]int{}
	for _, i := range args[2].([]interface{}) {
		m := []int{}
		for _, n := range i.([]interface{}) {
			m = append(m, reflectToInt(n))
		}
		match = append(match, m)
	}
	for i, item := range result {
		if i > len(f.items)-1 {
			height := 8 + 8 + editor.font.height
			width := f.width

			itemHandler := &SpanHandler{}
			itemSpan := ui.NewArea(itemHandler)
			itemHandler.span = itemSpan
			itemHandler.matchColor = newRGBA(29, 91, 145, 1)
			y := height * (i + 1)
			ui.QueueMain(func() {
				f.box.Append(itemSpan, false)
				itemSpan.SetSize(width, height)
				itemSpan.SetPosition(0, y)
			})

			f.items = append(f.items, &FinderItem{
				item: itemHandler,
			})
		}
		itemHandler := f.items[i]
		itemHandler.item.SetText(item.(string))
		itemHandler.item.SetFont(editor.font.font)
		itemHandler.item.paddingLeft = 10
		itemHandler.item.paddingRight = 10
		itemHandler.item.paddingTop = 8
		itemHandler.item.paddingBottom = 8
		fg := newRGBA(205, 211, 222, 1)
		itemHandler.item.SetColor(fg)
		itemHandler.item.match = f.patternText
		itemHandler.item.matchIndex = match[i]
		if i == selected {
			itemHandler.item.SetBackground(newRGBA(81, 154, 186, 1))
		} else {
			itemHandler.item.SetBackground(newRGBA(14, 17, 18, 1))
		}
		ui.QueueMain(func() {
			itemHandler.item.span.Show()
			itemHandler.item.span.QueueRedrawAll()
		})
	}
	for i := len(result); i < len(f.items); i++ {
		item := f.items[i]
		ui.QueueMain(func() {
			item.item.span.Hide()
		})
	}
}
<|endoftext|>


<commit_before>package finder

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"

	"github.com/gobwas/glob"
)

type itemType uint8

const (
	typeAll itemType = iota
	typeFile
	typeDir
)

// Finder contains options for a file/directory search.
type Finder struct {
	dirs        []string
	names       []Matcher
	paths       []Matcher
	notNames    []Matcher
	setupErrors []error
	itype       itemType
}

// Matcher checks whether an Item matches.
type Matcher func(Item) bool

// New returns a new finder.
//
// By default it will search for both files and directories.
func New() *Finder {
	return &Finder{}
}

// In searches in the given list of directories.
func (f *Finder) In(directories ...string) *Finder {
	f.dirs = append(f.dirs, directories...)
	return f
}

// Path narrows down the folders to be searched using gobwas/glob
//
// p is matched against the items RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) Path(p string) *Finder {
	g, err := glob.Compile(p, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	matcher := func(i Item) bool {
		if i.IsDir() {
			return g.Match(i.RelPath())
		}
		return g.Match(filepath.Dir(i.RelPath()))
	}
	f.paths = append(f.paths, matcher)
	return f
}

// Name matches a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) Name(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) name(n string) Matcher {
	g, err := glob.Compile(n, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return g.Match(i.Name())
	}
}

// NameRegex matches a file or directory name using package regexp.
func (f *Finder) NameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) nameRegex(n string) Matcher {
	re, err := regexp.Compile(n)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return re.MatchString(i.Name())
	}
}

// NotName excludes a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) NotName(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// NotNameRegex excludes a file or directory name using package regexp.
func (f *Finder) NotNameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// Files makes the finder return files only.
func (f *Finder) Files() *Finder {
	f.itype = typeFile
	return f
}

// Dirs makes the finder return directories only.
func (f *Finder) Dirs() *Finder {
	f.itype = typeDir
	return f
}

var errNoMatch = errors.New("Item did not match")
var errSkipDir = filepath.SkipDir

func (f *Finder) match(i Item) error {
	if (f.itype == typeDir && !i.IsDir()) || (f.itype == typeFile && i.IsDir()) {
		return errNoMatch
	}
	var match error
	if len(f.paths) > 0 {
		if i.IsDir() {
			for _, p := range f.paths {
				if !p(i) {
					return errSkipDir
				}
			}
		} else {
			match = errNoMatch
			for _, p := range f.paths {
				if p(i) {
					match = nil
				}
			}
			if match == errNoMatch {
				return match
			}
		}
	}
	if len(f.names) > 0 {
		match = errNoMatch
		for _, n := range f.names {
			if n(i) {
				match = nil
				break
			}
		}
	}
	if match == errNoMatch {
		return match
	}
	if len(f.notNames) > 0 {
		for _, matcher := range f.notNames {
			if matcher(i) {
				match = errNoMatch
				continue
			}
		}
	}
	return match
}

// Each calls func fn with each found item.
func (f *Finder) Each(fn func(Item)) []error {
	var errs []error
	var dir string
	walker := func(path string, info os.FileInfo, err error) error {
		if dir == path {
			return nil
		}
		if err != nil {
			return err
		}
		relDir, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		item := newItem(info, dir, relDir)
		match := f.match(item)
		switch match {
		case nil:
			fn(item)
			return nil
		case errNoMatch:
			return nil
		case errSkipDir:
			return errSkipDir
		}
		return nil
	}
	for _, dir = range f.dirs {
		err := filepath.Walk(dir, walker)
		if err != nil {
			errs = append(errs, err)
		}
	}
	return append(f.setupErrors, errs...)
}

// ToSlice returns a slice of all found items.
func (f *Finder) ToSlice() ([]Item, []error) {
	var l []Item
	errs := f.Each(func(file Item) {
		l = append(l, file)
	})
	return l, errs
}
<commit_msg>Ignore skipped directories<commit_after>package finder

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"

	"github.com/gobwas/glob"
)

type itemType uint8

const (
	typeAll itemType = iota
	typeFile
	typeDir
)

// Finder contains options for a file/directory search.
type Finder struct {
	dirs        []string
	names       []Matcher
	paths       []Matcher
	notNames    []Matcher
	setupErrors []error
	itype       itemType
}

// Matcher checks whether an Item matches.
type Matcher func(Item) bool

// New returns a new finder.
//
// By default it will search for both files and directories.
func New() *Finder {
	return &Finder{}
}

// In searches in the given list of directories.
func (f *Finder) In(directories ...string) *Finder {
	f.dirs = append(f.dirs, directories...)
	return f
}

// Path narrows down the folders to be searched using gobwas/glob
//
// p is matched against the items RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) Path(p string) *Finder {
	g, err := glob.Compile(p, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	matcher := func(i Item) bool {
		if i.IsDir() {
			return g.Match(i.RelPath())
		}
		return g.Match(filepath.Dir(i.RelPath()))
	}
	f.paths = append(f.paths, matcher)
	return f
}

// Name matches a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) Name(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) name(n string) Matcher {
	g, err := glob.Compile(n, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return g.Match(i.Name())
	}
}

// NameRegex matches a file or directory name using package regexp.
func (f *Finder) NameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) nameRegex(n string) Matcher {
	re, err := regexp.Compile(n)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return re.MatchString(i.Name())
	}
}

// NotName excludes a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) NotName(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// NotNameRegex excludes a file or directory name using package regexp.
func (f *Finder) NotNameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// Files makes the finder return files only.
func (f *Finder) Files() *Finder {
	f.itype = typeFile
	return f
}

// Dirs makes the finder return directories only.
func (f *Finder) Dirs() *Finder {
	f.itype = typeDir
	return f
}

var errNoMatch = errors.New("Item did not match")
var errSkipDir = filepath.SkipDir

func (f *Finder) match(i Item) error {
	if (f.itype == typeDir && !i.IsDir()) || (f.itype == typeFile && i.IsDir()) {
		return errNoMatch
	}
	var match error
	if len(f.paths) > 0 {
		if i.IsDir() {
			for _, p := range f.paths {
				if !p(i) {
					return errSkipDir
				}
			}
		} else {
			match = errNoMatch
			for _, p := range f.paths {
				if p(i) {
					match = nil
				}
			}
			if match == errNoMatch {
				return match
			}
		}
	}
	if len(f.names) > 0 {
		match = errNoMatch
		for _, n := range f.names {
			if n(i) {
				match = nil
				break
			}
		}
	}
	if match == errNoMatch {
		return match
	}
	if len(f.notNames) > 0 {
		for _, matcher := range f.notNames {
			if matcher(i) {
				match = errNoMatch
				continue
			}
		}
	}
	return match
}

// Each calls func fn with each found item.
func (f *Finder) Each(fn func(Item)) []error {
	var errs []error
	var dir string
	walker := func(path string, info os.FileInfo, err error) error {
		if dir == path {
			return nil
		}
		if err != nil {
			return err
		}
		relDir, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		item := newItem(info, dir, relDir)
		match := f.match(item)
		switch match {
		case nil:
			fn(item)
			return nil
		case errNoMatch:
			return nil
		case errSkipDir:
			return errSkipDir
		}
		return nil
	}
	for _, dir = range f.dirs {
		err := filepath.Walk(dir, walker)
		if err != nil && err != errSkipDir {
			errs = append(errs, err)
		}
	}
	return append(f.setupErrors, errs...)
}

// ToSlice returns a slice of all found items.
func (f *Finder) ToSlice() ([]Item, []error) {
	var l []Item
	errs := f.Each(func(file Item) {
		l = append(l, file)
	})
	return l, errs
}
<|endoftext|>


<commit_before>// Copyright (c) 2016,2017 Company 0, LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package session

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
	"sync"
	"testing"
	"time"

	"github.com/companyzero/zkc/zkidentity"
)

var (
	alice   *zkidentity.FullIdentity
	bob     *zkidentity.FullIdentity
	aliceKX *KX
	bobKX   *KX
)

var mtx sync.Mutex

func log(id int, format string, args ...interface{}) {
	mtx.Lock()
	defer mtx.Unlock()
	t := time.Now().Format(time.UnixDate)
	fmt.Fprintf(os.Stderr, t+" "+format+"\n", args...)
}

func loadIdentities(t *testing.T) (alice, bob *zkidentity.FullIdentity) {
	f, err := os.Open("testdata/alice.blob")
	if err != nil {
		panic(err)
	}
	blob1 := new([3076]byte)
	_, err = io.ReadFull(f, blob1[:])
	if err != nil {
		panic(err)
	}
	alice, err = zkidentity.UnmarshalFullIdentity(blob1[:])
	if err != nil {
		panic(err)
	}

	f, err = os.Open("testdata/bob.blob")
	if err != nil {
		panic(err)
	}
	blob2 := new([3072]byte)
	_, err = io.ReadFull(f, blob2[:])
	if err != nil {
		panic(err)
	}
	bob, err = zkidentity.UnmarshalFullIdentity(blob2[:])
	if err != nil {
		panic(err)
	}
	return alice, bob
}

func newIdentities(t *testing.T) (alice, bob *zkidentity.FullIdentity) {
	alice, err := zkidentity.New("Alice The Malice", "alice")
	if err != nil {
		panic(err)
	}
	bob, err = zkidentity.New("Bob The Builder", "bob")
	if err != nil {
		panic(err)
	}
	return alice, bob
}

func testKX(t *testing.T, alice, bob *zkidentity.FullIdentity) {
	loadIdentities(t)
	SetDiagnostic(log)

	Init()
	aliceKX := new(KX)
	aliceKX.MaxMessageSize = 4096
	aliceKX.OurPublicKey = &alice.Public.Key
	aliceKX.OurPrivateKey = &alice.PrivateKey
	aliceKX.TheirPublicKey = &bob.Public.Key
	t.Logf("alice fingerprint: %v", alice.Public.Fingerprint())

	bobKX := new(KX)
	bobKX.MaxMessageSize = 4096
	bobKX.OurPublicKey = &bob.Public.Key
	bobKX.OurPrivateKey = &bob.PrivateKey
	t.Logf("bob fingerprint: %v", bob.Public.Fingerprint())

	msg := []byte("this is a message of sorts")
	wg := sync.WaitGroup{}
	wg.Add(2)
	wait := make(chan bool)
	go func() {
		defer wg.Done()
		listener, err := net.Listen("tcp", "127.0.0.1:12346")
		if err != nil {
			wait <- false
			t.Fatal(err)
		}
		wait <- true // start client

		conn, err := listener.Accept()
		if err != nil {
			t.Fatal(err)
		}

		bobKX.Conn = conn
		err = bobKX.Respond()
		if err != nil {
			t.Fatal(err)
		}

		// read
		received, err := bobKX.Read()
		if err != nil {
			t.Fatal(err)
		}
		if !bytes.Equal(received, msg) {
			t.Fatalf("message not identical")
		}

		// write
		err = bobKX.Write(msg)
		if err != nil {
			t.Fatal(err)
		}
	}()

	ok := <-wait
	if !ok {
		t.Fatalf("server not started")
	}

	conn, err := net.Dial("tcp", "127.0.0.1:12346")
	if err != nil {
		t.Fatal(err)
	}

	aliceKX.Conn = conn
	err = aliceKX.Initiate()
	if err != nil {
		t.Fatalf("initiator %v", err)
	}

	err = aliceKX.Write(msg)
	if err != nil {
		t.Error(err)
		// fallthrough
	} else {

		// read
		received, err := aliceKX.Read()
		if err != nil {
			t.Error(err)
			// fallthrough
		} else {
			if !bytes.Equal(received, msg) {
				t.Errorf("message not identical")
				// fallthrough
			}
		}
	}

	wg.Done()
	wg.Wait()
}

func TestStaticIdentities(t *testing.T) {
	alice, bob := loadIdentities(t)
	testKX(t, alice, bob)
}

func TestRandomIdentities(t *testing.T) {
	alice, bob := newIdentities(t)
	testKX(t, alice, bob)
}
<commit_msg>close the listener instance in testKX()<commit_after>// Copyright (c) 2016,2017 Company 0, LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package session

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
	"sync"
	"testing"
	"time"

	"github.com/companyzero/zkc/zkidentity"
)

var (
	alice   *zkidentity.FullIdentity
	bob     *zkidentity.FullIdentity
	aliceKX *KX
	bobKX   *KX
)

var mtx sync.Mutex

func log(id int, format string, args ...interface{}) {
	mtx.Lock()
	defer mtx.Unlock()
	t := time.Now().Format(time.UnixDate)
	fmt.Fprintf(os.Stderr, t+" "+format+"\n", args...)
}

func loadIdentities(t *testing.T) (alice, bob *zkidentity.FullIdentity) {
	f, err := os.Open("testdata/alice.blob")
	if err != nil {
		panic(err)
	}
	blob1 := new([3076]byte)
	_, err = io.ReadFull(f, blob1[:])
	if err != nil {
		panic(err)
	}
	alice, err = zkidentity.UnmarshalFullIdentity(blob1[:])
	if err != nil {
		panic(err)
	}

	f, err = os.Open("testdata/bob.blob")
	if err != nil {
		panic(err)
	}
	blob2 := new([3072]byte)
	_, err = io.ReadFull(f, blob2[:])
	if err != nil {
		panic(err)
	}
	bob, err = zkidentity.UnmarshalFullIdentity(blob2[:])
	if err != nil {
		panic(err)
	}
	return alice, bob
}

func newIdentities(t *testing.T) (alice, bob *zkidentity.FullIdentity) {
	alice, err := zkidentity.New("Alice The Malice", "alice")
	if err != nil {
		panic(err)
	}
	bob, err = zkidentity.New("Bob The Builder", "bob")
	if err != nil {
		panic(err)
	}
	return alice, bob
}

func testKX(t *testing.T, alice, bob *zkidentity.FullIdentity) {
	loadIdentities(t)
	SetDiagnostic(log)

	Init()
	aliceKX := new(KX)
	aliceKX.MaxMessageSize = 4096
	aliceKX.OurPublicKey = &alice.Public.Key
	aliceKX.OurPrivateKey = &alice.PrivateKey
	aliceKX.TheirPublicKey = &bob.Public.Key
	t.Logf("alice fingerprint: %v", alice.Public.Fingerprint())

	bobKX := new(KX)
	bobKX.MaxMessageSize = 4096
	bobKX.OurPublicKey = &bob.Public.Key
	bobKX.OurPrivateKey = &bob.PrivateKey
	t.Logf("bob fingerprint: %v", bob.Public.Fingerprint())

	msg := []byte("this is a message of sorts")
	wg := sync.WaitGroup{}
	wg.Add(2)
	wait := make(chan bool)
	go func() {
		defer wg.Done()
		listener, err := net.Listen("tcp", "127.0.0.1:12346")
		if err != nil {
			wait <- false
			t.Fatal(err)
		}
		wait <- true // start client

		conn, err := listener.Accept()
		if err != nil {
			t.Fatal(err)
		}

		bobKX.Conn = conn
		err = bobKX.Respond()
		if err != nil {
			t.Fatal(err)
		}

		// read
		received, err := bobKX.Read()
		if err != nil {
			t.Fatal(err)
		}
		if !bytes.Equal(received, msg) {
			t.Fatalf("message not identical")
		}

		// write
		err = bobKX.Write(msg)
		if err != nil {
			t.Fatal(err)
		}

		listener.Close()
	}()

	ok := <-wait
	if !ok {
		t.Fatalf("server not started")
	}

	conn, err := net.Dial("tcp", "127.0.0.1:12346")
	if err != nil {
		t.Fatal(err)
	}

	aliceKX.Conn = conn
	err = aliceKX.Initiate()
	if err != nil {
		t.Fatalf("initiator %v", err)
	}

	err = aliceKX.Write(msg)
	if err != nil {
		t.Error(err)
		// fallthrough
	} else {

		// read
		received, err := aliceKX.Read()
		if err != nil {
			t.Error(err)
			// fallthrough
		} else {
			if !bytes.Equal(received, msg) {
				t.Errorf("message not identical")
				// fallthrough
			}
		}
	}

	wg.Done()
	wg.Wait()
}

func TestStaticIdentities(t *testing.T) {
	alice, bob := loadIdentities(t)
	testKX(t, alice, bob)
}

func TestRandomIdentities(t *testing.T) {
	alice, bob := newIdentities(t)
	testKX(t, alice, bob)
}
<|endoftext|>