<commit_before>package colly

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"net/http"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"github.com/PuerkitoBio/goquery"

	"github.com/gocolly/colly/debug"
)

var testServerPort = 31337
var testServerAddr = fmt.Sprintf("127.0.0.1:%d", testServerPort)
var testServerRootURL = fmt.Sprintf("http://%s/", testServerAddr)
var serverIndexResponse = []byte("hello world\n")
var robotsFile = `
User-agent: *
Allow: /allowed
Disallow: /disallowed
`

func init() {
	srv := &http.Server{}
	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: testServerPort})
	if err != nil {
		panic(err)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write(serverIndexResponse)
	})

	http.HandleFunc("/html", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Conent-Type", "text/html")
		w.Write([]byte(`<!DOCTYPE html>

<title>Test Page</title>

Hello World

This is a test page

This is a test paragraph

		`))
	})

	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			w.Header().Set("Conent-Type", "text/html")
			w.Write([]byte(r.FormValue("name")))
		}
	})

	http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Write([]byte(robotsFile))
	})

	http.HandleFunc("/allowed", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Write([]byte("allowed"))
	})

	http.HandleFunc("/disallowed", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Write([]byte("disallowed"))
	})

	http.Handle("/redirect", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/redirected/", http.StatusSeeOther)

	}))
	http.Handle("/redirected/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, `<a href="test">test</a>`)
	}))

	http.HandleFunc("/set_cookie", func(w http.ResponseWriter, r *http.Request) {
		c := &http.Cookie{Name: "test", Value: "testv", HttpOnly: false}
		http.SetCookie(w, c)
		w.WriteHeader(200)
		w.Write([]byte("ok"))
	})

	http.HandleFunc("/check_cookie", func(w http.ResponseWriter, r *http.Request) {
		cs := r.Cookies()
		if len(cs) != 1 || r.Cookies()[0].Value != "testv" {
			w.WriteHeader(500)
			w.Write([]byte("nok"))
			return
		}
		w.WriteHeader(200)
		w.Write([]byte("ok"))
	})

	go func() {
		if err := srv.Serve(listener); err != nil {
			log.Printf("Httpserver: ListenAndServe() error: %s", err)
		}
	}()
}

func TestNewCollector(t *testing.T) {
	t.Run("Functional Options", func(t *testing.T) {
		t.Run("UserAgent", func(t *testing.T) {
			for _, ua := range []string{
				"foo",
				"bar",
			} {
				c := NewCollector(UserAgent(ua))

				if got, want := c.UserAgent, ua; got != want {
					t.Fatalf("c.UserAgent = %q, want %q", got, want)
				}
			}
		})

		t.Run("MaxDepth", func(t *testing.T) {
			for _, depth := range []int{
				12,
				34,
				0,
			} {
				c := NewCollector(MaxDepth(depth))

				if got, want := c.MaxDepth, depth; got != want {
					t.Fatalf("c.MaxDepth = %d, want %d", got, want)
				}
			}
		})

		t.Run("AllowedDomains", func(t *testing.T) {
			for _, domains := range [][]string{
				[]string{"example.com", "example.net"},
				[]string{"example.net"},
				[]string{},
				nil,
			} {
				c := NewCollector(AllowedDomains(domains...))

				if got, want := c.AllowedDomains, domains; !reflect.DeepEqual(got, want) {
					t.Fatalf("c.AllowedDomains = %q, want %q", got, want)
				}
			}
		})

		t.Run("DisallowedDomains", func(t *testing.T) {
			for _, domains := range [][]string{
				[]string{"example.com", "example.net"},
				[]string{"example.net"},
				[]string{},
				nil,
			} {
				c := NewCollector(DisallowedDomains(domains...))

				if got, want := c.DisallowedDomains, domains; !reflect.DeepEqual(got, want) {
					t.Fatalf("c.DisallowedDomains = %q, want %q", got, want)
				}
			}
		})

		t.Run("URLFilters", func(t *testing.T) {
			for _, filters := range [][]*regexp.Regexp{
				[]*regexp.Regexp{regexp.MustCompile(`\w+`)},
				[]*regexp.Regexp{regexp.MustCompile(`\d+`)},
				[]*regexp.Regexp{},
				nil,
			} {
				c := NewCollector(URLFilters(filters...))

				if got, want := c.URLFilters, filters; !reflect.DeepEqual(got, want) {
					t.Fatalf("c.URLFilters = %v, want %v", got, want)
				}
			}
		})

		t.Run("AllowURLRevisit", func(t *testing.T) {
			c := NewCollector(AllowURLRevisit())

			if !c.AllowURLRevisit {
				t.Fatal("c.AllowURLRevisit = false, want true")
			}
		})

		t.Run("MaxBodySize", func(t *testing.T) {
			for _, sizeInBytes := range []int{
				1024 * 1024,
				1024,
				0,
			} {
				c := NewCollector(MaxBodySize(sizeInBytes))

				if got, want := c.MaxBodySize, sizeInBytes; got != want {
					t.Fatalf("c.MaxBodySize = %d, want %d", got, want)
				}
			}
		})

		t.Run("CacheDir", func(t *testing.T) {
			for _, path := range []string{
				"/tmp/",
				"/var/cache/",
			} {
				c := NewCollector(CacheDir(path))

				if got, want := c.CacheDir, path; got != want {
					t.Fatalf("c.CacheDir = %q, want %q", got, want)
				}
			}
		})

		t.Run("IgnoreRobotsTxt", func(t *testing.T) {
			c := NewCollector(IgnoreRobotsTxt())

			if !c.IgnoreRobotsTxt {
				t.Fatal("c.IgnoreRobotsTxt = false, want true")
			}
		})

		t.Run("ID", func(t *testing.T) {
			for _, id := range []uint32{
				0,
				1,
				2,
			} {
				c := NewCollector(ID(id))

				if got, want := c.ID, id; got != want {
					t.Fatalf("c.ID = %d, want %d", got, want)
				}
			}
		})

		t.Run("DetectCharset", func(t *testing.T) {
			c := NewCollector(DetectCharset())

			if !c.DetectCharset {
				t.Fatal("c.DetectCharset = false, want true")
			}
		})

		t.Run("Debugger", func(t *testing.T) {
			d := &debug.LogDebugger{}
			c := NewCollector(Debugger(d))

			if got, want := c.debugger, d; got != want {
				t.Fatalf("c.debugger = %v, want %v", got, want)
			}
		})
	})
}

func TestCollectorVisit(t *testing.T) {
	c := NewCollector()

	onRequestCalled := false
	onResponseCalled := false
	onScrapedCalled := false

	c.OnRequest(func(r *Request) {
		onRequestCalled = true
		r.Ctx.Put("x", "y")
	})

	c.OnResponse(func(r *Response) {
		onResponseCalled = true

		if r.Ctx.Get("x") != "y" {
			t.Error("Failed to retrieve context value for key 'x'")
		}

		if !bytes.Equal(r.Body, serverIndexResponse) {
			t.Error("Response body does not match with the original content")
		}
	})

	c.OnScraped(func(r *Response) {
		if !onResponseCalled {
			t.Error("OnScraped called before OnResponse")
		}

		if !onRequestCalled {
			t.Error("OnScraped called before OnRequest")
		}

		onScrapedCalled = true
	})

	c.Visit(testServerRootURL)

	if !onRequestCalled {
		t.Error("Failed to call OnRequest callback")
	}

	if !onResponseCalled {
		t.Error("Failed to call OnResponse callback")
	}

	if !onScrapedCalled {
		t.Error("Failed to call OnScraped callback")
	}
}

func TestCollectorOnHTML(t *testing.T) {
	c := NewCollector()

	titleCallbackCalled := false
	paragraphCallbackCount := 0

	c.OnHTML("title", func(e *HTMLElement) {
		titleCallbackCalled = true
		if e.Text != "Test Page" {
			t.Error("Title element text does not match, got", e.Text)
		}
	})

	c.OnHTML("p", func(e *HTMLElement) {
		paragraphCallbackCount++
		if e.Attr("class") != "description" {
			t.Error("Failed to get paragraph's class attribute")
		}
	})

	c.OnHTML("body", func(e *HTMLElement) {
		if e.ChildAttr("p", "class") != "description" {
			t.Error("Invalid class value")
		}
		classes := e.ChildAttrs("p", "class")
		if len(classes) != 2 {
			t.Error("Invalid class values")
		}
	})

	c.Visit(testServerRootURL + "html")

	if !titleCallbackCalled {
		t.Error("Failed to call OnHTML callback for <title> tag")
	}

	if paragraphCallbackCount != 2 {
		t.Error("Failed to find all
 tags")
	}
}

func TestCollectorURLRevisit(t *testing.T) {
	c := NewCollector()

	visitCount := 0

	c.OnRequest(func(r *Request) {
		visitCount++
	})

	c.Visit(testServerRootURL)
	c.Visit(testServerRootURL)

	if visitCount != 1 {
		t.Error("URL revisited")
	}

	c.AllowURLRevisit = true

	c.Visit(testServerRootURL)
	c.Visit(testServerRootURL)

	if visitCount != 3 {
		t.Error("URL not revisited")
	}
}

func TestCollectorPost(t *testing.T) {
	postValue := "hello"
	c := NewCollector()

	c.OnResponse(func(r *Response) {
		if postValue != string(r.Body) {
			t.Error("Failed to send data with POST")
		}
	})

	c.Post(testServerRootURL+"login", map[string]string{
		"name": postValue,
	})
}

func TestRedirect(t *testing.T) {
	c := NewCollector()
	c.OnHTML("a[href]", func(e *HTMLElement) {
		u := e.Request.AbsoluteURL(e.Attr("href"))
		if !strings.HasSuffix(u, "/redirected/test") {
			t.Error("Invalid URL after redirect: " + u)
		}
	})
	c.Visit(testServerRootURL + "redirect")
}

func TestCollectorCookies(t *testing.T) {
	c := NewCollector()

	if err := c.Visit(testServerRootURL + "set_cookie"); err != nil {
		t.Fatal(err)
	}

	if err := c.Visit(testServerRootURL + "check_cookie"); err != nil {
		t.Fatalf("Failed to use previously set cookies: %s", err)
	}
}

func BenchmarkVisit(b *testing.B) {
	c := NewCollector()
	c.OnHTML("p", func(_ *HTMLElement) {})

	for n := 0; n < b.N; n++ {
		c.Visit(fmt.Sprintf("%shtml?q=%d", testServerRootURL, n))
	}
}

func TestRobotsWhenAllowed(t *testing.T) {
	c := NewCollector()
	c.IgnoreRobotsTxt = false

	c.OnResponse(func(resp *Response) {
		if resp.StatusCode != 200 {
			t.Fatalf("Wrong response code: %d", resp.StatusCode)
		}
	})

	err := c.Visit(testServerRootURL + "allowed")

	if err != nil {
		t.Fatal(err)
	}
}

func TestRobotsWhenDisallowed(t *testing.T) {
	c := NewCollector()
	c.IgnoreRobotsTxt = false

	c.OnResponse(func(resp *Response) {
		t.Fatalf("Received response: %d", resp.StatusCode)
	})

	err := c.Visit(testServerRootURL + "disallowed")
	if err.Error() != "URL blocked by robots.txt" {
		t.Fatalf("wrong error message: %v", err)
	}
}

func TestIgnoreRobotsWhenDisallowed(t *testing.T) {
	c := NewCollector()
	c.IgnoreRobotsTxt = true

	c.OnResponse(func(resp *Response) {
		if resp.StatusCode != 200 {
			t.Fatalf("Wrong response code: %d", resp.StatusCode)
		}
	})

	err := c.Visit(testServerRootURL + "disallowed")

	if err != nil {
		t.Fatal(err)
	}

}

func TestHTMLElement(t *testing.T) {
	ctx := &Context{}
	resp := &Response{
		Request: &Request{
			Ctx: ctx,
		},
		Ctx: ctx,
	}

	in := `<a href="http://go-colly.org">Colly</a>`
	sel := "a[href]"
	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer([]byte(in)))
	if err != nil {
		t.Fatal(err)
	}
	elements := []*HTMLElement{}
	doc.Find(sel).Each(func(i int, s *goquery.Selection) {
		for _, n := range s.Nodes {
			elements = append(elements, NewHTMLElementFromSelectionNode(resp, s, n))
		}
	})
	elementsLen := len(elements)
	if elementsLen != 1 {
		t.Errorf("element length mismatch. got %d, expected %d.\n", elementsLen, 1)
	}
	v := elements[0]
	if v.Name != "a" {
		t.Errorf("element tag mismatch. got %s, expected %s.\n", v.Name, "a")
	}
	if v.Text != "Colly" {
		t.Errorf("element content mismatch. got %s, expected %s.\n", v.Text, "Colly")
	}
	if v.Attr("href") != "http://go-colly.org" {
		t.Errorf("element href mismatch. got %s, expected %s.\n", v.Attr("href"), "http://go-colly.org")
	}
}
<commit_msg>minor style fixes to satisfy goreportcard<commit_after>package colly

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"net/http"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"github.com/PuerkitoBio/goquery"

	"github.com/gocolly/colly/debug"
)

var testServerPort = 31337
var testServerAddr = fmt.Sprintf("127.0.0.1:%d", testServerPort)
var testServerRootURL = fmt.Sprintf("http://%s/", testServerAddr)
var serverIndexResponse = []byte("hello world\n")
var robotsFile = `
User-agent: *
Allow: /allowed
Disallow: /disallowed
`

func init() {
	srv := &http.Server{}
	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: testServerPort})
	if err != nil {
		panic(err)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write(serverIndexResponse)
	})

	http.HandleFunc("/html", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Conent-Type", "text/html")
		w.Write([]byte(`<!DOCTYPE html>

<title>Test Page</title>

Hello World

This is a test page

This is a test paragraph

		`))
	})

	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			w.Header().Set("Conent-Type", "text/html")
			w.Write([]byte(r.FormValue("name")))
		}
	})

	http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Write([]byte(robotsFile))
	})

	http.HandleFunc("/allowed", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Write([]byte("allowed"))
	})

	http.HandleFunc("/disallowed", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Write([]byte("disallowed"))
	})

	http.Handle("/redirect", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/redirected/", http.StatusSeeOther)

	}))
	http.Handle("/redirected/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, `<a href="test">test</a>`)
	}))

	http.HandleFunc("/set_cookie", func(w http.ResponseWriter, r *http.Request) {
		c := &http.Cookie{Name: "test", Value: "testv", HttpOnly: false}
		http.SetCookie(w, c)
		w.WriteHeader(200)
		w.Write([]byte("ok"))
	})

	http.HandleFunc("/check_cookie", func(w http.ResponseWriter, r *http.Request) {
		cs := r.Cookies()
		if len(cs) != 1 || r.Cookies()[0].Value != "testv" {
			w.WriteHeader(500)
			w.Write([]byte("nok"))
			return
		}
		w.WriteHeader(200)
		w.Write([]byte("ok"))
	})

	go func() {
		if err := srv.Serve(listener); err != nil {
			log.Printf("Httpserver: ListenAndServe() error: %s", err)
		}
	}()
}

var newCollectorTests = map[string]func(*testing.T){
	"URLFilters": func(t *testing.T) {
		for _, filters := range [][]*regexp.Regexp{
			{regexp.MustCompile(`\w+`)},
			{regexp.MustCompile(`\d+`)},
			{},
			nil,
		} {
			c := NewCollector(URLFilters(filters...))

			if got, want := c.URLFilters, filters; !reflect.DeepEqual(got, want) {
				t.Fatalf("c.URLFilters = %v, want %v", got, want)
			}
		}
	},
	"AllowURLRevisit": func(t *testing.T) {
		c := NewCollector(AllowURLRevisit())

		if !c.AllowURLRevisit {
			t.Fatal("c.AllowURLRevisit = false, want true")
		}
	},
	"MaxBodySize": func(t *testing.T) {
		for _, sizeInBytes := range []int{
			1024 * 1024,
			1024,
			0,
		} {
			c := NewCollector(MaxBodySize(sizeInBytes))

			if got, want := c.MaxBodySize, sizeInBytes; got != want {
				t.Fatalf("c.MaxBodySize = %d, want %d", got, want)
			}
		}
	},
	"IgnoreRobotsTxt": func(t *testing.T) {
		c := NewCollector(IgnoreRobotsTxt())

		if !c.IgnoreRobotsTxt {
			t.Fatal("c.IgnoreRobotsTxt = false, want true")
		}
	},
	"ID": func(t *testing.T) {
		for _, id := range []uint32{
			0,
			1,
			2,
		} {
			c := NewCollector(ID(id))

			if got, want := c.ID, id; got != want {
				t.Fatalf("c.ID = %d, want %d", got, want)
			}
		}
	},
	"DetectCharset": func(t *testing.T) {
		c := NewCollector(DetectCharset())

		if !c.DetectCharset {
			t.Fatal("c.DetectCharset = false, want true")
		}
	},
	"Debugger": func(t *testing.T) {
		d := &debug.LogDebugger{}
		c := NewCollector(Debugger(d))

		if got, want := c.debugger, d; got != want {
			t.Fatalf("c.debugger = %v, want %v", got, want)
		}
	},
}

func TestNewCollector(t *testing.T) {
	for n, f := range newCollectorTests {
		t.Run(n, f)
	}
}

func TestCollectorVisit(t *testing.T) {
	c := NewCollector()

	onRequestCalled := false
	onResponseCalled := false
	onScrapedCalled := false

	c.OnRequest(func(r *Request) {
		onRequestCalled = true
		r.Ctx.Put("x", "y")
	})

	c.OnResponse(func(r *Response) {
		onResponseCalled = true

		if r.Ctx.Get("x") != "y" {
			t.Error("Failed to retrieve context value for key 'x'")
		}

		if !bytes.Equal(r.Body, serverIndexResponse) {
			t.Error("Response body does not match with the original content")
		}
	})

	c.OnScraped(func(r *Response) {
		if !onResponseCalled {
			t.Error("OnScraped called before OnResponse")
		}

		if !onRequestCalled {
			t.Error("OnScraped called before OnRequest")
		}

		onScrapedCalled = true
	})

	c.Visit(testServerRootURL)

	if !onRequestCalled {
		t.Error("Failed to call OnRequest callback")
	}

	if !onResponseCalled {
		t.Error("Failed to call OnResponse callback")
	}

	if !onScrapedCalled {
		t.Error("Failed to call OnScraped callback")
	}
}

func TestCollectorOnHTML(t *testing.T) {
	c := NewCollector()

	titleCallbackCalled := false
	paragraphCallbackCount := 0

	c.OnHTML("title", func(e *HTMLElement) {
		titleCallbackCalled = true
		if e.Text != "Test Page" {
			t.Error("Title element text does not match, got", e.Text)
		}
	})

	c.OnHTML("p", func(e *HTMLElement) {
		paragraphCallbackCount++
		if e.Attr("class") != "description" {
			t.Error("Failed to get paragraph's class attribute")
		}
	})

	c.OnHTML("body", func(e *HTMLElement) {
		if e.ChildAttr("p", "class") != "description" {
			t.Error("Invalid class value")
		}
		classes := e.ChildAttrs("p", "class")
		if len(classes) != 2 {
			t.Error("Invalid class values")
		}
	})

	c.Visit(testServerRootURL + "html")

	if !titleCallbackCalled {
		t.Error("Failed to call OnHTML callback for <title> tag")
	}

	if paragraphCallbackCount != 2 {
		t.Error("Failed to find all
 tags")
	}
}

func TestCollectorURLRevisit(t *testing.T) {
	c := NewCollector()

	visitCount := 0

	c.OnRequest(func(r *Request) {
		visitCount++
	})

	c.Visit(testServerRootURL)
	c.Visit(testServerRootURL)

	if visitCount != 1 {
		t.Error("URL revisited")
	}

	c.AllowURLRevisit = true

	c.Visit(testServerRootURL)
	c.Visit(testServerRootURL)

	if visitCount != 3 {
		t.Error("URL not revisited")
	}
}

func TestCollectorPost(t *testing.T) {
	postValue := "hello"
	c := NewCollector()

	c.OnResponse(func(r *Response) {
		if postValue != string(r.Body) {
			t.Error("Failed to send data with POST")
		}
	})

	c.Post(testServerRootURL+"login", map[string]string{
		"name": postValue,
	})
}

func TestRedirect(t *testing.T) {
	c := NewCollector()
	c.OnHTML("a[href]", func(e *HTMLElement) {
		u := e.Request.AbsoluteURL(e.Attr("href"))
		if !strings.HasSuffix(u, "/redirected/test") {
			t.Error("Invalid URL after redirect: " + u)
		}
	})
	c.Visit(testServerRootURL + "redirect")
}

func TestCollectorCookies(t *testing.T) {
	c := NewCollector()

	if err := c.Visit(testServerRootURL + "set_cookie"); err != nil {
		t.Fatal(err)
	}

	if err := c.Visit(testServerRootURL + "check_cookie"); err != nil {
		t.Fatalf("Failed to use previously set cookies: %s", err)
	}
}

func BenchmarkVisit(b *testing.B) {
	c := NewCollector()
	c.OnHTML("p", func(_ *HTMLElement) {})

	for n := 0; n < b.N; n++ {
		c.Visit(fmt.Sprintf("%shtml?q=%d", testServerRootURL, n))
	}
}

func TestRobotsWhenAllowed(t *testing.T) {
	c := NewCollector()
	c.IgnoreRobotsTxt = false

	c.OnResponse(func(resp *Response) {
		if resp.StatusCode != 200 {
			t.Fatalf("Wrong response code: %d", resp.StatusCode)
		}
	})

	err := c.Visit(testServerRootURL + "allowed")

	if err != nil {
		t.Fatal(err)
	}
}

func TestRobotsWhenDisallowed(t *testing.T) {
	c := NewCollector()
	c.IgnoreRobotsTxt = false

	c.OnResponse(func(resp *Response) {
		t.Fatalf("Received response: %d", resp.StatusCode)
	})

	err := c.Visit(testServerRootURL + "disallowed")
	if err.Error() != "URL blocked by robots.txt" {
		t.Fatalf("wrong error message: %v", err)
	}
}

func TestIgnoreRobotsWhenDisallowed(t *testing.T) {
	c := NewCollector()
	c.IgnoreRobotsTxt = true

	c.OnResponse(func(resp *Response) {
		if resp.StatusCode != 200 {
			t.Fatalf("Wrong response code: %d", resp.StatusCode)
		}
	})

	err := c.Visit(testServerRootURL + "disallowed")

	if err != nil {
		t.Fatal(err)
	}

}

func TestHTMLElement(t *testing.T) {
	ctx := &Context{}
	resp := &Response{
		Request: &Request{
			Ctx: ctx,
		},
		Ctx: ctx,
	}

	in := `<a href="http://go-colly.org">Colly</a>`
	sel := "a[href]"
	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer([]byte(in)))
	if err != nil {
		t.Fatal(err)
	}
	elements := []*HTMLElement{}
	doc.Find(sel).Each(func(i int, s *goquery.Selection) {
		for _, n := range s.Nodes {
			elements = append(elements, NewHTMLElementFromSelectionNode(resp, s, n))
		}
	})
	elementsLen := len(elements)
	if elementsLen != 1 {
		t.Errorf("element length mismatch. got %d, expected %d.\n", elementsLen, 1)
	}
	v := elements[0]
	if v.Name != "a" {
		t.Errorf("element tag mismatch. got %s, expected %s.\n", v.Name, "a")
	}
	if v.Text != "Colly" {
		t.Errorf("element content mismatch. got %s, expected %s.\n", v.Text, "Colly")
	}
	if v.Attr("href") != "http://go-colly.org" {
		t.Errorf("element href mismatch. got %s, expected %s.\n", v.Attr("href"), "http://go-colly.org")
	}
}
<|endoftext|>


<commit_before>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
	"os/exec"
	"bytes"
)

var templates = template.Must(template.ParseFiles("index.html"))

const MESSAGE_QUEUE_SIZE = 10

const STATUS_FAILURE = "{\"status\":\"failure\"}"

var authKey = []byte("somesecretauth")
var store sessions.Store

var pool *Pool
var clients map[int64]*Client

var db *sql.DB

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	otherid int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      []byte
	client1 *Client
	client2 *Client
	Question *Question
}

type Question struct {
	Id 	int64			`json:"id"`
	Title string		`json:"title"`
	Body string			`json:"body"`
	Difficulty int 		`json:"diff"`
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		for c1.id == c2.id {
			c2 = <- p.in
		}

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 32)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 32 {
			return
		}

		row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
		q := new(Question)
		err = row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)
		if err !=  nil {
			fmt.Println(err)
		}

		room := &Room{b, c1, c2, q}

		c1.otherid, c2.otherid = c2.id, c1.id
		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/question/new", newQuestion)
	http.HandleFunc("/question/submit", testCode)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/assets/"))))
	http.ListenAndServe(":8080", nil)
}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("join ", uid)

	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}

	clients[uid] = client
	pool.in <- client

	chatroom := <- retChan

	fmt.Println("joinChatRoom-chatroom.id: ", chatroom.id)

	qjs, err := json.Marshal(chatroom.Question)

	fmt.Println("qjs: ", string(qjs))

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":\"", asciify(chatroom.id), "\",\"question\":", string(qjs), "}")
}

func asciify(ba []byte) string {
	ret := make([]byte, len(ba))
	for i, b := range ba {
		ret[i] = (b % 26) + 97
	}
	return string(ret)
}

func testCode(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	code := r.FormValue("submission")
	_ = r.FormValue("cvid")
	qid := string(1)
	app := "./secure.sh"
	cmd := exec.Command(app, qid, code)
    var b bytes.Buffer
    cmd.Stdout = &b
    err := cmd.Run()
    if err != nil {
    	fmt.Println(err)
    }
    fmt.Fprint(w, "{\"status\":\"success\", \"data\": \'", b.String(), "\'}" )
    return
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	client := clients[uid]

	if client != nil {
		fmt.Println("leaving")
		delete(clients, client.otherid)
		delete(clients, uid)
	}

	fmt.Println("leave ", uid)

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("send ", uid)

	r.ParseForm()
	message := r.FormValue("s")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, STATUS_FAILURE)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("check ", uid)

	client := clients[uid]

	if client != nil {
		select {
		case message, ok := <- client.in:
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"", message, "\"}")
			} else {
				delete(clients, uid)
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"\"}")
		}

	} else {
		fmt.Fprint(w, "{\"status\":\"failure\",\"s\":\"\"}")
	}
}

type User struct {
    Id            	int64      	`json:"id"`
    FacebookId  	string		`json:fbid"`
    Username 		string		`json:username"`
    Email 			string		`json:email"`
    Level 			int64		`json:lvl"`
    Score 			int64		`json:score"`
}

func newQuestion(w http.ResponseWriter, r *http.Request) {
	row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
	q := new(Question)
	err := row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)

	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	b, err := json.Marshal(q)
	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	fmt.Fprint(w, string(b))
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		user := new(User)
		err := row.Scan(&user.Id)

		if err != nil {
			_, err = db.Exec("INSERT INTO users (facebook_id, username, email, level, points) VALUES (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, STATUS_FAILURE)
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&user.Id)
				if err != nil {
					fmt.Fprint(w, STATUS_FAILURE)
					return
				}
			}

		}

		session, _ := store.Get(r, "session")
		session.Values["userid"] = user.Id
		session.Save(r, w)

		fmt.Fprint(w, "{\"status\":\"success\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>adding changes to bash call<commit_after>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
	"os/exec"
	"bytes"
)

var templates = template.Must(template.ParseFiles("index.html"))

const MESSAGE_QUEUE_SIZE = 10

const STATUS_FAILURE = "{\"status\":\"failure\"}"

var authKey = []byte("somesecretauth")
var store sessions.Store

var pool *Pool
var clients map[int64]*Client

var db *sql.DB

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	otherid int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      []byte
	client1 *Client
	client2 *Client
	Question *Question
}

type Question struct {
	Id 	int64			`json:"id"`
	Title string		`json:"title"`
	Body string			`json:"body"`
	Difficulty int 		`json:"diff"`
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		for c1.id == c2.id {
			c2 = <- p.in
		}

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 32)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 32 {
			return
		}

		row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
		q := new(Question)
		err = row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)
		if err !=  nil {
			fmt.Println(err)
		}

		room := &Room{b, c1, c2, q}

		c1.otherid, c2.otherid = c2.id, c1.id
		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/question/new", newQuestion)
	http.HandleFunc("/question/submit", testCode)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/assets/"))))
	http.ListenAndServe(":8080", nil)
}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("join ", uid)

	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}

	clients[uid] = client
	pool.in <- client

	chatroom := <- retChan

	fmt.Println("joinChatRoom-chatroom.id: ", chatroom.id)

	qjs, err := json.Marshal(chatroom.Question)

	fmt.Println("qjs: ", string(qjs))

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":\"", asciify(chatroom.id), "\",\"question\":", string(qjs), "}")
}

func asciify(ba []byte) string {
	ret := make([]byte, len(ba))
	for i, b := range ba {
		ret[i] = (b % 26) + 97
	}
	return string(ret)
}

func testCode(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	code := r.FormValue("submission")
	_ = r.FormValue("cvid")
	qid := string(1)
	app := "./secure.sh"
	cmd := exec.Command(app, qid, code)
    var b bytes.Buffer
    cmd.Stdout = &b
    err := cmd.Run()
    if err != nil {
    	fmt.Println(err)
    }
    fmt.Fprint(w, "{\"status\":\"success\", \"data\": \"", b.String(), "\"}" )
    return
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	client := clients[uid]

	if client != nil {
		fmt.Println("leaving")
		delete(clients, client.otherid)
		delete(clients, uid)
	}

	fmt.Println("leave ", uid)

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("send ", uid)

	r.ParseForm()
	message := r.FormValue("s")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, STATUS_FAILURE)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("check ", uid)

	client := clients[uid]

	if client != nil {
		select {
		case message, ok := <- client.in:
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"", message, "\"}")
			} else {
				delete(clients, uid)
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"\"}")
		}

	} else {
		fmt.Fprint(w, "{\"status\":\"failure\",\"s\":\"\"}")
	}
}

type User struct {
    Id            	int64      	`json:"id"`
    FacebookId  	string		`json:fbid"`
    Username 		string		`json:username"`
    Email 			string		`json:email"`
    Level 			int64		`json:lvl"`
    Score 			int64		`json:score"`
}

func newQuestion(w http.ResponseWriter, r *http.Request) {
	row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
	q := new(Question)
	err := row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)

	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	b, err := json.Marshal(q)
	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	fmt.Fprint(w, string(b))
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		user := new(User)
		err := row.Scan(&user.Id)

		if err != nil {
			_, err = db.Exec("INSERT INTO users (facebook_id, username, email, level, points) VALUES (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, STATUS_FAILURE)
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&user.Id)
				if err != nil {
					fmt.Fprint(w, STATUS_FAILURE)
					return
				}
			}

		}

		session, _ := store.Get(r, "session")
		session.Values["userid"] = user.Id
		session.Save(r, w)

		fmt.Fprint(w, "{\"status\":\"success\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package humanize

import (
	"testing"
)

func TestCommas(t *testing.T) {
	assert(t, "0", Comma(0), "0")
	assert(t, "10", Comma(10), "10")
	assert(t, "100", Comma(100), "100")
	assert(t, "1,000", Comma(1000), "1,000")
	assert(t, "10,000", Comma(10000), "10,000")
	assert(t, "10,000,000", Comma(10000000), "10,000,000")
	assert(t, "123,456,789", Comma(123456789), "123,456,789")
	assert(t, "-123,456,789", Comma(-123456789), "-123,456,789")
	assert(t, "-10,000,000", Comma(-10000000), "-10,000,000")
	assert(t, "-10,000", Comma(-10000), "-10,000")
	assert(t, "-1,000", Comma(-1000), "-1,000")
	assert(t, "-100", Comma(-100), "-100")
	assert(t, "-10", Comma(-10), "-10")
}

func BenchmarkCommas(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Comma(1000000000)
		Comma(1234567890)
	}
}
<commit_msg>Increase coverage of comma code.<commit_after>package humanize

import (
	"testing"
)

func TestCommas(t *testing.T) {
	assert(t, "0", Comma(0), "0")
	assert(t, "10", Comma(10), "10")
	assert(t, "100", Comma(100), "100")
	assert(t, "1,000", Comma(1000), "1,000")
	assert(t, "10,000", Comma(10000), "10,000")
	assert(t, "100,000", Comma(100000), "100,000")
	assert(t, "10,000,000", Comma(10000000), "10,000,000")
	assert(t, "10,100,000", Comma(10100000), "10,100,000")
	assert(t, "10,010,000", Comma(10010000), "10,010,000")
	assert(t, "10,001,000", Comma(10001000), "10,001,000")
	assert(t, "123,456,789", Comma(123456789), "123,456,789")
	assert(t, "-123,456,789", Comma(-123456789), "-123,456,789")
	assert(t, "-10,100,000", Comma(-10100000), "-10,100,000")
	assert(t, "-10,010,000", Comma(-10010000), "-10,010,000")
	assert(t, "-10,001,000", Comma(-10001000), "-10,001,000")
	assert(t, "-10,000,000", Comma(-10000000), "-10,000,000")
	assert(t, "-100,000", Comma(-100000), "-100,000")
	assert(t, "-10,000", Comma(-10000), "-10,000")
	assert(t, "-1,000", Comma(-1000), "-1,000")
	assert(t, "-100", Comma(-100), "-100")
	assert(t, "-10", Comma(-10), "-10")
}

func BenchmarkCommas(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Comma(1000000000)
		Comma(1234567890)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 by Dobrosław Żybort. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package slug

import (
	"github.com/fiam/gounidecode/unidecode"
	"regexp"
	"strings"
)

var (
	// Custom substitution map
	CustomSub map[rune]string
)

//=============================================================================

// Make returns slug generated from provided string. Will use "en" as language
// substitution.
func Make(s string) (slug string) {
	return MakeLang(s, "en")
}

// MakeLang returns slug generated from provided string and will use provided
// language for chars substitution.
func MakeLang(s string, lang string) (slug string) {
	slug = strings.TrimSpace(s)

	// Select substitution language
	switch lang {
	case "de":
		slug = Substitute(slug, deSub)
	case "en":
		slug = Substitute(slug, enSub)
	case "pl":
		slug = Substitute(slug, plSub)
	default: // fallback to "en" if lang not found
		slug = Substitute(slug, enSub)
	}

	slug = Substitute(slug, defaultSub)
	slug = Substitute(slug, CustomSub)

	slug = unidecode.Unidecode(slug)

	slug = strings.ToLower(slug)

	slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-")
	slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-")
	slug = strings.Trim(slug, "-")
	return slug
}

// Substitute string chars with provided substitution map.
func Substitute(s string, sub map[rune]string) (buf string) {
	for _, c := range s {
		if d, ok := sub[c]; ok {
			buf += d
		} else {
			buf += string(c)
		}
	}
	return
}
<commit_msg>Change: split substitution (string and rune), add CustomRuneSub<commit_after>// Copyright 2013 by Dobrosław Żybort. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package slug

import (
	"github.com/fiam/gounidecode/unidecode"
	"regexp"
	"strings"
)

var (
	// Custom substitution map
	CustomSub map[string]string
	// Custom rune substitution map
	CustomRuneSub map[rune]string
)

//=============================================================================

// Make returns slug generated from provided string. Will use "en" as language
// substitution.
func Make(s string) (slug string) {
	return MakeLang(s, "en")
}

// MakeLang returns slug generated from provided string and will use provided
// language for chars substitution.
func MakeLang(s string, lang string) (slug string) {
	slug = strings.TrimSpace(s)

	// Select substitution language
	switch lang {
	case "de":
		slug = SubstituteRune(slug, deSub)
	case "en":
		slug = SubstituteRune(slug, enSub)
	case "pl":
		slug = SubstituteRune(slug, plSub)
	default: // fallback to "en" if lang not found
		slug = SubstituteRune(slug, enSub)
	}

	slug = SubstituteRune(slug, defaultSub)

	// Custom substitutions
	slug = SubstituteRune(slug, CustomRuneSub)
	slug = Substitute(slug, CustomSub)

	slug = unidecode.Unidecode(slug)

	slug = strings.ToLower(slug)

	slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-")
	slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-")
	slug = strings.Trim(slug, "-")
	return slug
}

// Substitute returns string with superseded all substrings from
// provided substitution map.
func Substitute(s string, sub map[string]string) (buf string) {
	buf = s
	for key, val := range sub {
		buf = strings.Replace(s, key, val, -1)
	}
	return
}

// SubstituteRune substitutes string chars with provided rune
// substitution map.
func SubstituteRune(s string, sub map[rune]string) (buf string) {
	for _, c := range s {
		if d, ok := sub[c]; ok {
			buf += d
		} else {
			buf += string(c)
		}
	}
	return
}
<|endoftext|>


<commit_before>package vcd

import (
	"fmt"
	"github.com/hashicorp/terraform/helper/schema"
	types "github.com/hmrc/vmware-govcd/types/v56"
	"log"
)

func resourceVcdVApp() *schema.Resource {
	return &schema.Resource{
		Create: resourceVcdVAppCreate,
		Update: resourceVcdVAppUpdate,
		Read:   resourceVcdVAppRead,
		Delete: resourceVcdVAppDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"template_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"catalog_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"network_href": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"network_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"memory": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"cpus": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"initscript": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"metadata": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
			"href": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"power_on": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
			},
		},
	}
}

func resourceVcdVAppCreate(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)

	catalog, err := vcdClient.Org.FindCatalog(d.Get("catalog_name").(string))
	if err != nil {
		return fmt.Errorf("Error finding catalog: %#v", err)
	}

	catalogitem, err := catalog.FindCatalogItem(d.Get("template_name").(string))
	if err != nil {
		return fmt.Errorf("Error finding catelog item: %#v", err)
	}

	vapptemplate, err := catalogitem.GetVAppTemplate()
	if err != nil {
		return fmt.Errorf("Error finding VAppTemplate: %#v", err)
	}

	log.Printf("[DEBUG] VAppTemplate: %#v", vapptemplate)
	var networkHref string
	net, err := vcdClient.OrgVdc.FindVDCNetwork(d.Get("network_name").(string))
	if err != nil {
		return fmt.Errorf("Error finding OrgVCD Network: %#v", err)
	}
	if attr, ok := d.GetOk("network_href"); ok {
		networkHref = attr.(string)
	} else {
		networkHref = net.OrgVDCNetwork.HREF
	}
	// vapptemplate := govcd.NewVAppTemplate(&vcdClient.Client)
	//
	createvapp := &types.InstantiateVAppTemplateParams{
		Ovf:   "http://schemas.dmtf.org/ovf/envelope/1",
		Xmlns: "http://www.vmware.com/vcloud/v1.5",
		Name:  d.Get("name").(string),
		InstantiationParams: &types.InstantiationParams{
			NetworkConfigSection: &types.NetworkConfigSection{
				Info: "Configuration parameters for logical networks",
				NetworkConfig: &types.VAppNetworkConfiguration{
					NetworkName: d.Get("network_name").(string),
					Configuration: &types.NetworkConfiguration{
						ParentNetwork: &types.Reference{
							HREF: networkHref,
						},
						FenceMode: "bridged",
					},
				},
			},
		},
		Source: &types.Reference{
			HREF: vapptemplate.VAppTemplate.HREF,
		},
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		e := vcdClient.OrgVdc.InstantiateVAppTemplate(createvapp)

		if e != nil {
			return fmt.Errorf("Error: %#v", e)
		}

		e = vcdClient.OrgVdc.Refresh()
		if e != nil {
			return fmt.Errorf("Error: %#v", e)
		}
		return nil
	})
	if err != nil {
		return err
	}

	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Get("name").(string))

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.ChangeVMName(d.Get("name").(string))
		if err != nil {
			return fmt.Errorf("Error with vm name change: %#v", err)
		}

		return task.WaitTaskCompletion()
	})
	if err != nil {
		return fmt.Errorf("Error changing vmname: %#v", err)
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.ChangeNetworkConfig(d.Get("network_name").(string), d.Get("ip").(string))
		if err != nil {
			return fmt.Errorf("Error with Networking change: %#v", err)
		}
		return task.WaitTaskCompletion()
	})
	if err != nil {
		return fmt.Errorf("Error changing network: %#v", err)
	}

	if initscript, ok := d.GetOk("initscript"); ok {
		err = retryCall(vcdClient.MaxRetryTimeout, func() error {
			task, err := vapp.RunCustomizationScript(d.Get("name").(string), initscript.(string))
			if err != nil {
				return fmt.Errorf("Error with setting init script: %#v", err)
			}
			return task.WaitTaskCompletion()
		})
		if err != nil {
			return fmt.Errorf("Error completing tasks: %#v", err)
		}
	}

	d.SetId(d.Get("name").(string))

	return resourceVcdVAppUpdate(d, meta)
}

func resourceVcdVAppUpdate(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)
	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())

	if err != nil {
		return fmt.Errorf("Error finding VApp: %#v", err)
	}

	status, err := vapp.GetStatus()
	if err != nil {
		return fmt.Errorf("Error getting VApp status: %#v", err)
	}

	if d.HasChange("metadata") {
		oraw, nraw := d.GetChange("metadata")
		metadata := oraw.(map[string]interface{})
		for k := range metadata {
			task, err := vapp.DeleteMetadata(k)
			if err != nil {
				return fmt.Errorf("Error deleting metadata: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}
		metadata = nraw.(map[string]interface{})
		for k, v := range metadata {
			task, err := vapp.AddMetadata(k, v.(string))
			if err != nil {
				return fmt.Errorf("Error adding metadata: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}

	}

	if d.HasChange("memory") || d.HasChange("cpus") || d.HasChange("power_on") {
		if status != "POWERED_OFF" {
			task, err := vapp.PowerOff()
			if err != nil {
				return fmt.Errorf("Error Powering Off: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}

		if d.HasChange("memory") {
			err = retryCall(vcdClient.MaxRetryTimeout, func() error {
				task, err := vapp.ChangeMemorySize(d.Get("memory").(int))
				if err != nil {
					return fmt.Errorf("Error changing memory size: %#v", err)
				}

				return task.WaitTaskCompletion()
			})
			if err != nil {
				return err
			}
		}

		if d.HasChange("cpus") {
			err = retryCall(vcdClient.MaxRetryTimeout, func() error {
				task, err := vapp.ChangeCPUcount(d.Get("cpus").(int))
				if err != nil {
					return fmt.Errorf("Error changing cpu count: %#v", err)
				}

				return task.WaitTaskCompletion()
			})
			if err != nil {
				return fmt.Errorf("Error completing task: %#v", err)
			}
		}

		if d.Get("power_on").(bool) {
			task, err := vapp.PowerOn()
			if err != nil {
				return fmt.Errorf("Error Powering Up: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}

	}

	return resourceVcdVAppRead(d, meta)
}

func resourceVcdVAppRead(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)

	err := vcdClient.OrgVdc.Refresh()
	if err != nil {
		return fmt.Errorf("Error refreshing vdc: %#v", err)
	}

	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())
	if err != nil {
		log.Printf("[DEBUG] Unable to find vapp. Removing from tfstate")
		d.SetId("")
		return nil
	}
	d.Set("ip", vapp.VApp.Children.VM[0].NetworkConnectionSection.NetworkConnection.IPAddress)

	return nil
}

func resourceVcdVAppDelete(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)
	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())

	if err != nil {
		return fmt.Errorf("error finding vapp: %s", err)
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.Undeploy()
		if err != nil {
			return fmt.Errorf("Error undeploying: %#v", err)
		}

		return task.WaitTaskCompletion()
	})
	if err != nil {
		return err
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.Delete()
		if err != nil {
			return fmt.Errorf("Error deleting: %#v", err)
		}

		return task.WaitTaskCompletion()
	})

	return err
}
<commit_msg>Only undeploy a machine if it is switched on<commit_after>package vcd

import (
	"fmt"
	"github.com/hashicorp/terraform/helper/schema"
	types "github.com/hmrc/vmware-govcd/types/v56"
	"log"
)

func resourceVcdVApp() *schema.Resource {
	return &schema.Resource{
		Create: resourceVcdVAppCreate,
		Update: resourceVcdVAppUpdate,
		Read:   resourceVcdVAppRead,
		Delete: resourceVcdVAppDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"template_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"catalog_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"network_href": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"network_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"memory": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"cpus": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"initscript": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"metadata": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
			"href": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"power_on": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
			},
		},
	}
}

func resourceVcdVAppCreate(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)

	catalog, err := vcdClient.Org.FindCatalog(d.Get("catalog_name").(string))
	if err != nil {
		return fmt.Errorf("Error finding catalog: %#v", err)
	}

	catalogitem, err := catalog.FindCatalogItem(d.Get("template_name").(string))
	if err != nil {
		return fmt.Errorf("Error finding catelog item: %#v", err)
	}

	vapptemplate, err := catalogitem.GetVAppTemplate()
	if err != nil {
		return fmt.Errorf("Error finding VAppTemplate: %#v", err)
	}

	log.Printf("[DEBUG] VAppTemplate: %#v", vapptemplate)
	var networkHref string
	net, err := vcdClient.OrgVdc.FindVDCNetwork(d.Get("network_name").(string))
	if err != nil {
		return fmt.Errorf("Error finding OrgVCD Network: %#v", err)
	}
	if attr, ok := d.GetOk("network_href"); ok {
		networkHref = attr.(string)
	} else {
		networkHref = net.OrgVDCNetwork.HREF
	}
	// vapptemplate := govcd.NewVAppTemplate(&vcdClient.Client)
	//
	createvapp := &types.InstantiateVAppTemplateParams{
		Ovf:   "http://schemas.dmtf.org/ovf/envelope/1",
		Xmlns: "http://www.vmware.com/vcloud/v1.5",
		Name:  d.Get("name").(string),
		InstantiationParams: &types.InstantiationParams{
			NetworkConfigSection: &types.NetworkConfigSection{
				Info: "Configuration parameters for logical networks",
				NetworkConfig: &types.VAppNetworkConfiguration{
					NetworkName: d.Get("network_name").(string),
					Configuration: &types.NetworkConfiguration{
						ParentNetwork: &types.Reference{
							HREF: networkHref,
						},
						FenceMode: "bridged",
					},
				},
			},
		},
		Source: &types.Reference{
			HREF: vapptemplate.VAppTemplate.HREF,
		},
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		e := vcdClient.OrgVdc.InstantiateVAppTemplate(createvapp)

		if e != nil {
			return fmt.Errorf("Error: %#v", e)
		}

		e = vcdClient.OrgVdc.Refresh()
		if e != nil {
			return fmt.Errorf("Error: %#v", e)
		}
		return nil
	})
	if err != nil {
		return err
	}

	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Get("name").(string))

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.ChangeVMName(d.Get("name").(string))
		if err != nil {
			return fmt.Errorf("Error with vm name change: %#v", err)
		}

		return task.WaitTaskCompletion()
	})
	if err != nil {
		return fmt.Errorf("Error changing vmname: %#v", err)
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.ChangeNetworkConfig(d.Get("network_name").(string), d.Get("ip").(string))
		if err != nil {
			return fmt.Errorf("Error with Networking change: %#v", err)
		}
		return task.WaitTaskCompletion()
	})
	if err != nil {
		return fmt.Errorf("Error changing network: %#v", err)
	}

	if initscript, ok := d.GetOk("initscript"); ok {
		err = retryCall(vcdClient.MaxRetryTimeout, func() error {
			task, err := vapp.RunCustomizationScript(d.Get("name").(string), initscript.(string))
			if err != nil {
				return fmt.Errorf("Error with setting init script: %#v", err)
			}
			return task.WaitTaskCompletion()
		})
		if err != nil {
			return fmt.Errorf("Error completing tasks: %#v", err)
		}
	}

	d.SetId(d.Get("name").(string))

	return resourceVcdVAppUpdate(d, meta)
}

func resourceVcdVAppUpdate(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)
	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())

	if err != nil {
		return fmt.Errorf("Error finding VApp: %#v", err)
	}

	status, err := vapp.GetStatus()
	if err != nil {
		return fmt.Errorf("Error getting VApp status: %#v", err)
	}

	if d.HasChange("metadata") {
		oraw, nraw := d.GetChange("metadata")
		metadata := oraw.(map[string]interface{})
		for k := range metadata {
			task, err := vapp.DeleteMetadata(k)
			if err != nil {
				return fmt.Errorf("Error deleting metadata: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}
		metadata = nraw.(map[string]interface{})
		for k, v := range metadata {
			task, err := vapp.AddMetadata(k, v.(string))
			if err != nil {
				return fmt.Errorf("Error adding metadata: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}

	}

	if d.HasChange("memory") || d.HasChange("cpus") || d.HasChange("power_on") {
		if status != "POWERED_OFF" {
			task, err := vapp.PowerOff()
			if err != nil {
				return fmt.Errorf("Error Powering Off: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}

		if d.HasChange("memory") {
			err = retryCall(vcdClient.MaxRetryTimeout, func() error {
				task, err := vapp.ChangeMemorySize(d.Get("memory").(int))
				if err != nil {
					return fmt.Errorf("Error changing memory size: %#v", err)
				}

				return task.WaitTaskCompletion()
			})
			if err != nil {
				return err
			}
		}

		if d.HasChange("cpus") {
			err = retryCall(vcdClient.MaxRetryTimeout, func() error {
				task, err := vapp.ChangeCPUcount(d.Get("cpus").(int))
				if err != nil {
					return fmt.Errorf("Error changing cpu count: %#v", err)
				}

				return task.WaitTaskCompletion()
			})
			if err != nil {
				return fmt.Errorf("Error completing task: %#v", err)
			}
		}

		if d.Get("power_on").(bool) {
			task, err := vapp.PowerOn()
			if err != nil {
				return fmt.Errorf("Error Powering Up: %#v", err)
			}
			err = task.WaitTaskCompletion()
			if err != nil {
				return fmt.Errorf("Error completing tasks: %#v", err)
			}
		}

	}

	return resourceVcdVAppRead(d, meta)
}

func resourceVcdVAppRead(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)

	err := vcdClient.OrgVdc.Refresh()
	if err != nil {
		return fmt.Errorf("Error refreshing vdc: %#v", err)
	}

	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())
	if err != nil {
		log.Printf("[DEBUG] Unable to find vapp. Removing from tfstate")
		d.SetId("")
		return nil
	}
	d.Set("ip", vapp.VApp.Children.VM[0].NetworkConnectionSection.NetworkConnection.IPAddress)

	return nil
}

func resourceVcdVAppDelete(d *schema.ResourceData, meta interface{}) error {
	vcdClient := meta.(*VCDClient)
	vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())

	if err != nil {
		return fmt.Errorf("error finding vapp: %s", err)
	}

	status, err := vapp.GetStatus()
	if err != nil {
		return fmt.Errorf("Error getting VApp status: %#v", err)
	}

	if status == "POWERED_ON" {
		err = retryCall(vcdClient.MaxRetryTimeout, func() error {
			task, err := vapp.Undeploy()
			if err != nil {
				return fmt.Errorf("Error undeploying: %#v", err)
			}

			return task.WaitTaskCompletion()
		})
		if err != nil {
			return err
		}
	}

	err = retryCall(vcdClient.MaxRetryTimeout, func() error {
		task, err := vapp.Delete()
		if err != nil {
			return fmt.Errorf("Error deleting: %#v", err)
		}

		return task.WaitTaskCompletion()
	})

	return err
}
<|endoftext|>


<commit_before>package cfg_test

import (
	"bytes"
	"testing"

	"github.com/walle/cfg"
)

const configString = `
# This is a comment

# An integer value
answer = 42

# A float value
pi = 3.14
# A boolean value
is_active = true

# A string value
quotes = Alea iacta est\nEt tu, Brute?
`

const configStringWithEmpty = `Answer = 42
Pi = 0
is_active = false
quotes =
`

type MyConfig struct {
	Answer     int
	Pi         float64
	IsActive   bool   `cfg:"is_active"`
	Quotes     string `cfg:"quotes"`
	unexported string
	NotUsed    string `cfg:"-"`
}

func Test_Unmarshal(t *testing.T) {
	myConfig := &MyConfig{}
	err := cfg.Unmarshal([]byte(configString), myConfig)
	if err != nil {
		t.Errorf("Error unmarshaling data: %s\n", err)
	}

	if myConfig.Answer != 42 {
		t.Errorf("Expected %v, got %v\n", 42, myConfig.Answer)
	}

	if myConfig.Pi != 3.14 {
		t.Errorf("Expected %v, got %v\n", 3.14, myConfig.Pi)
	}

	if myConfig.IsActive != true {
		t.Errorf("Expected %v, got %v\n", true, myConfig.IsActive)
	}

	q := "Alea iacta est\nEt tu, Brute?"
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}

func Test_UnmarshalWithEmpty(t *testing.T) {
	myConfig := &MyConfig{}
	err := cfg.Unmarshal([]byte(configStringWithEmpty), myConfig)
	if err != nil {
		t.Errorf("Error unmarshaling data: %s\n", err)
	}

	if myConfig.Answer != 42 {
		t.Errorf("Expected %v, got %v\n", 42, myConfig.Answer)
	}

	if myConfig.Pi != 0 {
		t.Errorf("Expected %v, got %v\n", 0, myConfig.Pi)
	}

	if myConfig.IsActive != false {
		t.Errorf("Expected %v, got %v\n", false, myConfig.IsActive)
	}

	q := ""
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}

func Test_UnmarshalFromConfig(t *testing.T) {
	myConfig := &MyConfig{}
	config, err := cfg.NewConfigFromReader(bytes.NewBufferString(configString))
	if err != nil {
		t.Errorf("Error parsing the config: %s\n", err)
	}

	err = cfg.UnmarshalFromConfig(config, myConfig)
	if err != nil {
		t.Errorf("Error unmarshaling data: %s\n", err)
	}

	if myConfig.Answer != 42 {
		t.Errorf("Expected %v, got %v\n", 42, myConfig.Answer)
	}

	if myConfig.Pi != 3.14 {
		t.Errorf("Expected %v, got %v\n", 3.14, myConfig.Pi)
	}

	if myConfig.IsActive != true {
		t.Errorf("Expected %v, got %v\n", true, myConfig.IsActive)
	}

	q := "Alea iacta est\nEt tu, Brute?"
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}

func Test_UnmarshalToNotStruct(t *testing.T) {
	var i int
	err := cfg.Unmarshal([]byte(configString), i)
	if err == nil {
		t.Errorf("Did not get error when trying to unmarshal to int\n")
	}
}
<commit_msg>Add test case for quoted string behaviour<commit_after>package cfg_test

import (
	"bytes"
	"testing"

	"github.com/walle/cfg"
)

const configString = `
# This is a comment

# An integer value
answer = 42

# A float value
pi = 3.14
# A boolean value
is_active = true

# A string value
quotes = Alea iacta est\nEt tu, Brute?
`

const configStringWithEmpty = `Answer = 42
Pi = 0
is_active = false
quotes =
`

type MyConfig struct {
	Answer     int
	Pi         float64
	IsActive   bool   `cfg:"is_active"`
	Quotes     string `cfg:"quotes"`
	unexported string
	NotUsed    string `cfg:"-"`
}

func Test_Unmarshal(t *testing.T) {
	myConfig := &MyConfig{}
	err := cfg.Unmarshal([]byte(configString), myConfig)
	if err != nil {
		t.Errorf("Error unmarshaling data: %s\n", err)
	}

	if myConfig.Answer != 42 {
		t.Errorf("Expected %v, got %v\n", 42, myConfig.Answer)
	}

	if myConfig.Pi != 3.14 {
		t.Errorf("Expected %v, got %v\n", 3.14, myConfig.Pi)
	}

	if myConfig.IsActive != true {
		t.Errorf("Expected %v, got %v\n", true, myConfig.IsActive)
	}

	q := "Alea iacta est\nEt tu, Brute?"
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}

func Test_UnmarshalWithEmpty(t *testing.T) {
	myConfig := &MyConfig{}
	err := cfg.Unmarshal([]byte(configStringWithEmpty), myConfig)
	if err != nil {
		t.Errorf("Error unmarshaling data: %s\n", err)
	}

	if myConfig.Answer != 42 {
		t.Errorf("Expected %v, got %v\n", 42, myConfig.Answer)
	}

	if myConfig.Pi != 0 {
		t.Errorf("Expected %v, got %v\n", 0, myConfig.Pi)
	}

	if myConfig.IsActive != false {
		t.Errorf("Expected %v, got %v\n", false, myConfig.IsActive)
	}

	q := ""
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}

func Test_UnmarshalFromConfig(t *testing.T) {
	myConfig := &MyConfig{}
	config, err := cfg.NewConfigFromReader(bytes.NewBufferString(configString))
	if err != nil {
		t.Errorf("Error parsing the config: %s\n", err)
	}

	err = cfg.UnmarshalFromConfig(config, myConfig)
	if err != nil {
		t.Errorf("Error unmarshaling data: %s\n", err)
	}

	if myConfig.Answer != 42 {
		t.Errorf("Expected %v, got %v\n", 42, myConfig.Answer)
	}

	if myConfig.Pi != 3.14 {
		t.Errorf("Expected %v, got %v\n", 3.14, myConfig.Pi)
	}

	if myConfig.IsActive != true {
		t.Errorf("Expected %v, got %v\n", true, myConfig.IsActive)
	}

	q := "Alea iacta est\nEt tu, Brute?"
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}

func Test_UnmarshalToNotStruct(t *testing.T) {
	var i int
	err := cfg.Unmarshal([]byte(configString), i)
	if err == nil {
		t.Errorf("Did not get error when trying to unmarshal to int\n")
	}
}

func Test_UnmarshalQuotedString(t *testing.T) {
	myConfig := &MyConfig{}
	conf := `quotes = "a quoted string"`
	err := cfg.Unmarshal([]byte(conf), myConfig)
	if err != nil {
		t.Errorf("Error parsing the config: %s\n", err)
	}
	q := `"a quoted string"`
	if myConfig.Quotes != q {
		t.Errorf("Expected %q, got %q\n", q, myConfig.Quotes)
	}
}
<|endoftext|>


<commit_before>package sssa

import (
	"math/big"
)

/**
 * Returns a new arary of secret shares (encoding x,y pairs as base64 strings)
 * created by Shamir's Secret Sharing Algorithm requring a minimum number of
 * share to recreate, of length shares, from the input secret raw as a string
**/
func Create(minimum int, shares int, raw string) []string {
	if minimum > shares {
		return []string{""}
	}

	var secret []*big.Int = splitByteToInt([]byte(raw))
	prime, _ = big.NewInt(0).SetString("99995644905598542077721161034987774965417302630805822064337798850767846245779", 10)
	var numbers []*big.Int = make([]*big.Int, 0)

	numbers = append(numbers, big.NewInt(0))

	var polynomial [][]*big.Int = make([][]*big.Int, len(secret))
	for i := range polynomial {
		polynomial[i] = make([]*big.Int, minimum)
		polynomial[i][0] = secret[i]

		for j := range polynomial[i][1:] {
			number := random()
			for inNumbers(numbers, number) {
				number = random()
			}
			numbers = append(numbers, number)

			polynomial[i][j+1] = number
		}
	}

	var secrets [][][]*big.Int = make([][][]*big.Int, shares)
	var result []string = make([]string, shares)
	for i := range secrets {
		secrets[i] = make([][]*big.Int, len(secret))
		for j := range secrets[i] {
			secrets[i][j] = make([]*big.Int, 2)

			number := random()
			for inNumbers(numbers, number) {
				number = random()
			}
			numbers = append(numbers, number)

			secrets[i][j][0] = number
			secrets[i][j][1] = evaluatePolynomial(polynomial[j], number)

			result[i] += toBase64(secrets[i][j][0])
			result[i] += toBase64(secrets[i][j][1])
		}
	}

	return result
}

/**
 * Takes a string array of shares encoded in base64 created via Shamir's
 * Algorithm; each string must be of equal length of a multiple of 88 characters
 * as a single 88 character share is a pair of 256-bit numbers (x, y).
 *
 * Note: the polynomial will converge if the specified minimum number of shares
 *       or more are passed to this function. Passing thus does not affect it
 *       Passing fewer however, simply means that the returned secret is wrong.
**/
func Combine(shares []string) string {
	var secrets [][][]*big.Int = make([][][]*big.Int, len(shares))
	prime, _ = big.NewInt(0).SetString("99995644905598542077721161034987774965417302630805822064337798850767846245779", 10)

	for i := range shares {
		if (len(shares[i]) % 88) != 0 {
			return ""
		}

		share := shares[i]
		count := len(share) / 88
		secrets[i] = make([][]*big.Int, count)

		for j := range secrets[i] {
			cshare := share[j*88 : (j+1)*88]
			secrets[i][j] = make([]*big.Int, 2)
			secrets[i][j][0] = fromBase64(cshare[0:44])
			secrets[i][j][1] = fromBase64(cshare[44:])
		}
	}

	var secret []*big.Int = make([]*big.Int, len(secrets[0]))
	for j := range secret {
		secret[j] = big.NewInt(0)
		for i := range secrets {
			origin := secrets[i][j][0]
			originy := secrets[i][j][1]
			numerator := big.NewInt(1)
			denominator := big.NewInt(1)
			for k := range secrets {
				if k != i {
					current := secrets[k][j][0]
					negative := big.NewInt(0)
					negative = negative.Mul(current, big.NewInt(-1))
					added := big.NewInt(0)
					added = added.Sub(origin, current)

					numerator = numerator.Mul(numerator, negative)
					numerator = numerator.Mod(numerator, prime)

					denominator = denominator.Mul(denominator, added)
					denominator = denominator.Mod(denominator, prime)
				}
			}

			working := big.NewInt(0).Set(originy)
			working = working.Mul(working, numerator)
			working = working.Mul(working, modInverse(denominator))
			working = working.Add(working, prime)

			secret[j] = secret[j].Add(secret[j], working)
			secret[j] = secret[j].Mod(secret[j], prime)
		}
	}

	return string(mergeIntToByte(secret))
}
<commit_msg>SSS implementation documentation.<commit_after>package sssa

import (
	"math/big"
)

/**
 * Returns a new arary of secret shares (encoding x,y pairs as base64 strings)
 * created by Shamir's Secret Sharing Algorithm requring a minimum number of
 * share to recreate, of length shares, from the input secret raw as a string
**/
func Create(minimum int, shares int, raw string) []string {
	// Verify minimum isn't greater than shares; there is no way to recreate
	// the original polynomial in our current setup, therefore it doesn't make
	// sense to generate fewer shares than are needed to reconstruct the secret.
	// [TODO]: proper error handling
	if minimum > shares {
		return []string{""}
	}

	// Convert the secret to its respective 256-bit big.Int representation
	var secret []*big.Int = splitByteToInt([]byte(raw))

	// Set constant prime across the package
	prime, _ = big.NewInt(0).SetString("99995644905598542077721161034987774965417302630805822064337798850767846245779", 10)

	// List of currently used numbers in the polynomial
	var numbers []*big.Int = make([]*big.Int, 0)
	numbers = append(numbers, big.NewInt(0))

	// Create the polynomial of degree (minimum - 1); that is, the highest
	// order term is (minimum-1), though as there is a constant term with
	// order 0, there are (minimum) number of coefficients.
	//
	// However, the polynomial object is a 2d array, because we are constructing
	// a different polynomial for each part of the secret
	// polynomial[parts][minimum]
	var polynomial [][]*big.Int = make([][]*big.Int, len(secret))
	for i := range polynomial {
		polynomial[i] = make([]*big.Int, minimum)
		polynomial[i][0] = secret[i]

		for j := range polynomial[i][1:] {
			// Each coefficient should be unique
			number := random()
			for inNumbers(numbers, number) {
				number = random()
			}
			numbers = append(numbers, number)

			polynomial[i][j+1] = number
		}
	}

	// Create the secrets object; this holds the (x, y) points of each share.
	// Again, because secret is an array, each share could have multiple parts
	// over which we are computing Shamir's Algorithm. The last dimension is
	// always two, as it is storing an x, y pair of points.
	//
	// Note: this array is technically unnecessary due to creating result
	// in the inner loop. Can disappear later if desired. [TODO]
	//
	// secrets[shares][parts][2]
	var secrets [][][]*big.Int = make([][][]*big.Int, shares)
	var result []string = make([]string, shares)

	// For every share...
	for i := range secrets {
		secrets[i] = make([][]*big.Int, len(secret))
		// ...and every part of the secret...
		for j := range secrets[i] {
			secrets[i][j] = make([]*big.Int, 2)

			// ...generate a new x-coordinate...
			number := random()
			for inNumbers(numbers, number) {
				number = random()
			}
			numbers = append(numbers, number)

			// ...and evaluate the polynomial at that point...
			secrets[i][j][0] = number
			secrets[i][j][1] = evaluatePolynomial(polynomial[j], number)

			// ...add it to results...
			result[i] += toBase64(secrets[i][j][0])
			result[i] += toBase64(secrets[i][j][1])
		}
	}

	// ...and return!
	return result
}

/**
 * Takes a string array of shares encoded in base64 created via Shamir's
 * Algorithm; each string must be of equal length of a multiple of 88 characters
 * as a single 88 character share is a pair of 256-bit numbers (x, y).
 *
 * Note: the polynomial will converge if the specified minimum number of shares
 *       or more are passed to this function. Passing thus does not affect it
 *       Passing fewer however, simply means that the returned secret is wrong.
**/
func Combine(shares []string) string {
	// Recreate the original object of x, y points, based upon number of shares
	// and size of each share (number of parts in the secret).
	var secrets [][][]*big.Int = make([][][]*big.Int, len(shares))

	// Set constant prime
	prime, _ = big.NewInt(0).SetString("99995644905598542077721161034987774965417302630805822064337798850767846245779", 10)

	// For each share...
	for i := range shares {
		// ...ensure that it is valid...
		if (len(shares[i]) % 88) != 0 {
			return ""
		}

		// ...find the number of parts it represents...
		share := shares[i]
		count := len(share) / 88
		secrets[i] = make([][]*big.Int, count)

		// ...and for each part, find the x,y pair...
		for j := range secrets[i] {
			cshare := share[j*88 : (j+1)*88]
			secrets[i][j] = make([]*big.Int, 2)
			// ...decoding from base 64.
			secrets[i][j][0] = fromBase64(cshare[0:44])
			secrets[i][j][1] = fromBase64(cshare[44:])
		}
	}

	// Use Lagrange Polynomial Interpolation (LPI) to reconstruct the secret.
	// For each part of the secert (clearest to iterate over)...
	var secret []*big.Int = make([]*big.Int, len(secrets[0]))
	for j := range secret {
		secret[j] = big.NewInt(0)
		// ...and every share...
		for i := range secrets { // LPI sum loop
			// ...remember the current x and y values...
			origin := secrets[i][j][0]
			originy := secrets[i][j][1]
			numerator := big.NewInt(1) // LPI numerator
			denominator := big.NewInt(1) // LPI denominator
			// ...and for every other point...
			for k := range secrets { // LPI product loop
				if k != i {
					// ...combine them via half products...
					current := secrets[k][j][0]
					negative := big.NewInt(0)
					negative = negative.Mul(current, big.NewInt(-1))
					added := big.NewInt(0)
					added = added.Sub(origin, current)

					numerator = numerator.Mul(numerator, negative)
					numerator = numerator.Mod(numerator, prime)

					denominator = denominator.Mul(denominator, added)
					denominator = denominator.Mod(denominator, prime)
				}
			}

			// LPI product
			// ...multiply together the points (y)(numerator)(denominator)^-1...
			working := big.NewInt(0).Set(originy)
			working = working.Mul(working, numerator)
			working = working.Mul(working, modInverse(denominator))
			working = working.Add(working, prime)

			// LPI sum
			secret[j] = secret[j].Add(secret[j], working)
			secret[j] = secret[j].Mod(secret[j], prime)
		}
	}

	// ...and return the result!
	return string(mergeIntToByte(secret))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

// On performance limited OS X hosts (ex: VMs) the iPhone/iOS Simulator might time out
//  while booting. So far it seems that a simple retry solves these issues.

// This boot timeout can happen when running Unit Tests
//  with Xcode Command Line `xcodebuild`.
const timeOutMessageIPhoneSimulator = "iPhoneSimulator: Timed out waiting"

// This boot timeout can happen when running Xcode (7+) UI tests
//  with Xcode Command Line `xcodebuild`.
const timeOutMessageUITest = "Terminating app due to uncaught exception '_XCTestCaseInterruptionException'"

func exportEnvironmentWithEnvman(keyStr, valueStr string) error {
	log.Printf("Exporting: %s", keyStr)
	envman := exec.Command("envman", "add", "--key", keyStr)
	envman.Stdin = strings.NewReader(valueStr)
	envman.Stdout = os.Stdout
	envman.Stderr = os.Stderr
	return envman.Run()
}

func printConfig(projectPath, scheme, simulatorDevice, simulatorOsVersion, action, deviceDestination string, cleanBuild bool) {
	log.Println()
	log.Println("========== Configs ==========")
	log.Printf(" * project_path: %s", projectPath)
	log.Printf(" * scheme: %s", scheme)
	log.Printf(" * simulator_device: %s", simulatorDevice)
	log.Printf(" * simulator_os_version: %s", simulatorOsVersion)
	log.Printf(" * is_clean_build: %v", cleanBuild)
	log.Printf(" * project_action: %s", action)
	log.Printf(" * device_destination: %s", deviceDestination)

	cmd := exec.Command("xcodebuild", "-version")
	xcodebuildVersion, err := cmd.CombinedOutput()
	if err != nil {
		log.Printf(" [!] Failed to get the version of xcodebuild! Error: %s", err)
	}
	fmt.Println()
	log.Println(" * xcodebuildVersion:")
	fmt.Printf("%s\n", xcodebuildVersion)
	fmt.Println()
}

func validateRequiredInput(key string) (string, error) {
	value := os.Getenv(key)
	if value == "" {
		return "", fmt.Errorf("[!] Missing required input: %s", key)
	}
	return value, nil
}

func isStringFoundInOutput(searchStr, outputToSearchIn string) bool {
	r, err := regexp.Compile("(?i)" + searchStr)
	if err != nil {
		log.Printf(" [!] Failed to compile regexp: %s", err)
		return false
	}
	return r.MatchString(outputToSearchIn)
}

func findFirstDelimiter(searchIn string, searchForDelimiters []string) (foundIdx int, foundDelim string) {
	foundIdx = -1
	for _, aDelim := range searchForDelimiters {
		aDelimFoundIdx := strings.Index(searchIn, aDelim)
		if aDelimFoundIdx >= 0 {
			if foundIdx == -1 || aDelimFoundIdx < foundIdx {
				foundIdx = aDelimFoundIdx
				foundDelim = aDelim
			}
		}
	}
	return foundIdx, foundDelim
}

func findTestSummaryInOutput(fullOutput string, isRunSucess bool) string {
	// using a list of possible delimiters, because the actual order
	//  of these delimiters varies in Xcode CLT's output,
	//  so we'll try to find the first occurance of any of the listed
	//  delimiters
	possibleDelimiters := []string{}
	if !isRunSucess {
		// Failed
		possibleDelimiters = []string{"Testing failed:", "Failing tests:", "** TEST FAILED **"}
	} else {
		// Success
		possibleDelimiters = []string{"Test Suite ", "** TEST SUCCEEDED **"}
	}

	splitIdx, _ := findFirstDelimiter(fullOutput, possibleDelimiters)
	if splitIdx < 0 {
		log.Println(" [!] Could not find any of the required test result delimiters")
		return ""
	}
	return fullOutput[splitIdx:]
}

func printableCommandArgs(fullCommandArgs []string) string {
	cmdArgsDecorated := []string{}
	for idx, anArg := range fullCommandArgs {
		quotedArg := strconv.Quote(anArg)
		if idx == 0 {
			quotedArg = anArg
		}
		cmdArgsDecorated = append(cmdArgsDecorated, quotedArg)
	}

	return strings.Join(cmdArgsDecorated, " ")
}

func runTest(action, projectPath, scheme string, cleanBuild bool, deviceDestination string, isRetryOnTimeout, isFullOutputMode bool) (string, error) {
	args := []string{action, projectPath, "-scheme", scheme}
	if cleanBuild {
		args = append(args, "clean")
	}
	// the 'build' argument is required *before* the 'test' arg, to prevent
	//  the Xcode bug described in the README, which causes:
	// 'iPhoneSimulator: Timed out waiting 120 seconds for simulator to boot, current state is 1.'
	//  in case the compilation takes a long time.
	// Related Radar link: https://openradar.appspot.com/22413115
	// Demonstration project: https://github.com/bitrise-io/simulator-launch-timeout-includes-build-time
	args = append(args, "build", "test", "-destination", deviceDestination, "-sdk", "iphonesimulator")
	cmd := exec.Command("xcodebuild", args...)

	var outBuffer bytes.Buffer
	var outWriter io.Writer
	if isFullOutputMode {
		outWriter = io.MultiWriter(&outBuffer, os.Stdout)
	} else {
		outWriter = &outBuffer
	}

	cmd.Stdin = nil
	cmd.Stdout = outWriter
	cmd.Stderr = outWriter

	fmt.Println()
	log.Println("=> Compiling and running the tests...")
	cmdArgsForPrint := printableCommandArgs(cmd.Args)

	log.Printf("==> Full command: %s", cmdArgsForPrint)
	if !isFullOutputMode {
		fmt.Println()
		log.Println("=> You selected to only see test results.")
		log.Println("   This can take some time, especially if the code have to be compiled first.")
	}

	runErr := cmd.Run()
	fullOutputStr := outBuffer.String()
	if runErr != nil {
		if isStringFoundInOutput(timeOutMessageIPhoneSimulator, fullOutputStr) {
			log.Println("=> Simulator Timeout detected")
			if isRetryOnTimeout {
				log.Println("==> isRetryOnTimeout=true - retrying...")
				return runTest(action, projectPath, scheme, false, deviceDestination, false, isFullOutputMode)
			}
			log.Println(" [!] isRetryOnTimeout=false, no more retry, stopping the test!")
			return fullOutputStr, runErr
		}

		if isStringFoundInOutput(timeOutMessageUITest, fullOutputStr) {
			log.Println("=> Simulator Timeout detected: isUITestTimeoutFound")
			if isRetryOnTimeout {
				log.Println("==> isRetryOnTimeout=true - retrying...")
				return runTest(action, projectPath, scheme, false, deviceDestination, false, isFullOutputMode)
			}
			log.Println(" [!] isRetryOnTimeout=false, no more retry, stopping the test!")
			return fullOutputStr, runErr
		}

		return fullOutputStr, runErr
	}

	return fullOutputStr, nil
}

func main() {
	//
	// Required parameters
	projectPath, err := validateRequiredInput("project_path")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	scheme, err := validateRequiredInput("scheme")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	simulatorDevice, err := validateRequiredInput("simulator_device")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	simulatorOsVersion, err := validateRequiredInput("simulator_os_version")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	//
	// Not required parameters
	cleanBuild := false
	if os.Getenv("is_clean_build") == "yes" {
		cleanBuild = true
	}
	isFullOutputMode := !(os.Getenv("is_full_output") == "no")

	//
	// Project-or-Workspace flag
	action := ""
	if strings.HasSuffix(projectPath, ".xcodeproj") {
		action = "-project"
	} else if strings.HasSuffix(projectPath, ".xcworkspace") {
		action = "-workspace"
	} else {
		log.Fatalf("Failed to get valid project file (invalid project file): %s", projectPath)
	}

	//
	// Device Destination
	// xcodebuild -project ./BitriseSampleWithYML.xcodeproj -scheme BitriseSampleWithYML  test -destination "platform=iOS Simulator,name=iPhone 6 Plus,OS=latest" -sdk iphonesimulator -verbose
	deviceDestination := fmt.Sprintf("platform=iOS Simulator,name=%s,OS=%s", simulatorDevice, simulatorOsVersion)

	//
	// Print configs
	printConfig(projectPath, scheme, simulatorDevice, simulatorOsVersion, action, deviceDestination, cleanBuild)

	//
	// Run
	fullOutputStr, runErr := runTest(action, projectPath, scheme, cleanBuild, deviceDestination, true, isFullOutputMode)

	//
	isRunSuccess := (runErr == nil)
	if isRunSuccess {
		exportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "succeeded")
	} else {
		exportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed")
	}
	testResultsSummary := findTestSummaryInOutput(fullOutputStr, isRunSuccess)
	if testResultsSummary == "" {
		testResultsSummary = fmt.Sprintf(" [!] No test summary found in the output - most likely it was a compilation error.\n\n Full output was: %s", fullOutputStr)
	}
	exportEnvironmentWithEnvman("BITRISE_XCODE_TEST_FULL_RESULTS_TEXT", testResultsSummary)

	if !isFullOutputMode {
		fmt.Println()
		fmt.Println("========= TEST RESULTS: =========")
		fmt.Println(testResultsSummary)
		fmt.Println()
	}

	if runErr != nil {
		log.Fatalf("xcode test failed with error: %s", runErr)
	}
}
<commit_msg>step fix<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

// On performance limited OS X hosts (ex: VMs) the iPhone/iOS Simulator might time out
//  while booting. So far it seems that a simple retry solves these issues.

// This boot timeout can happen when running Unit Tests
//  with Xcode Command Line `xcodebuild`.
const timeOutMessageIPhoneSimulator = "iPhoneSimulator: Timed out waiting"

// This boot timeout can happen when running Xcode (7+) UI tests
//  with Xcode Command Line `xcodebuild`.
const timeOutMessageUITest = "Terminating app due to uncaught exception '_XCTestCaseInterruptionException'"

func exportEnvironmentWithEnvman(keyStr, valueStr string) error {
	log.Printf("Exporting: %s", keyStr)
	envman := exec.Command("envman", "add", "--key", keyStr)
	envman.Stdin = strings.NewReader(valueStr)
	envman.Stdout = os.Stdout
	envman.Stderr = os.Stderr
	return envman.Run()
}

func printConfig(projectPath, scheme, simulatorDevice, simulatorOsVersion, action, deviceDestination string, cleanBuild bool) {
	log.Println()
	log.Println("========== Configs ==========")
	log.Printf(" * project_path: %s", projectPath)
	log.Printf(" * scheme: %s", scheme)
	log.Printf(" * simulator_device: %s", simulatorDevice)
	log.Printf(" * simulator_os_version: %s", simulatorOsVersion)
	log.Printf(" * is_clean_build: %v", cleanBuild)
	log.Printf(" * project_action: %s", action)
	log.Printf(" * device_destination: %s", deviceDestination)

	cmd := exec.Command("xcodebuild", "-version")
	xcodebuildVersion, err := cmd.CombinedOutput()
	if err != nil {
		log.Printf(" [!] Failed to get the version of xcodebuild! Error: %s", err)
	}
	fmt.Println()
	log.Println(" * xcodebuildVersion:")
	fmt.Printf("%s\n", xcodebuildVersion)
	fmt.Println()
}

func validateRequiredInput(key string) (string, error) {
	value := os.Getenv(key)
	if value == "" {
		return "", fmt.Errorf("[!] Missing required input: %s", key)
	}
	return value, nil
}

func isStringFoundInOutput(searchStr, outputToSearchIn string) bool {
	r, err := regexp.Compile("(?i)" + searchStr)
	if err != nil {
		log.Printf(" [!] Failed to compile regexp: %s", err)
		return false
	}
	return r.MatchString(outputToSearchIn)
}

func findFirstDelimiter(searchIn string, searchForDelimiters []string) (foundIdx int, foundDelim string) {
	foundIdx = -1
	for _, aDelim := range searchForDelimiters {
		aDelimFoundIdx := strings.Index(searchIn, aDelim)
		if aDelimFoundIdx >= 0 {
			if foundIdx == -1 || aDelimFoundIdx < foundIdx {
				foundIdx = aDelimFoundIdx
				foundDelim = aDelim
			}
		}
	}
	return foundIdx, foundDelim
}

func findTestSummaryInOutput(fullOutput string, isRunSucess bool) string {
	// using a list of possible delimiters, because the actual order
	//  of these delimiters varies in Xcode CLT's output,
	//  so we'll try to find the first occurance of any of the listed
	//  delimiters
	possibleDelimiters := []string{}
	if !isRunSucess {
		// Failed
		possibleDelimiters = []string{"Testing failed:", "Failing tests:", "** TEST FAILED **"}
	} else {
		// Success
		possibleDelimiters = []string{"Test Suite ", "** TEST SUCCEEDED **"}
	}

	splitIdx, _ := findFirstDelimiter(fullOutput, possibleDelimiters)
	if splitIdx < 0 {
		log.Println(" [!] Could not find any of the required test result delimiters")
		return ""
	}
	return fullOutput[splitIdx:]
}

func printableCommandArgs(fullCommandArgs []string) string {
	cmdArgsDecorated := []string{}
	for idx, anArg := range fullCommandArgs {
		quotedArg := strconv.Quote(anArg)
		if idx == 0 {
			quotedArg = anArg
		}
		cmdArgsDecorated = append(cmdArgsDecorated, quotedArg)
	}

	return strings.Join(cmdArgsDecorated, " ")
}

func runTest(action, projectPath, scheme string, cleanBuild bool, deviceDestination string, isRetryOnTimeout, isFullOutputMode bool) (string, error) {
	args := []string{action, projectPath, "-scheme", scheme}
	if cleanBuild {
		args = append(args, "clean")
	}
	// the 'build' argument is required *before* the 'test' arg, to prevent
	//  the Xcode bug described in the README, which causes:
	// 'iPhoneSimulator: Timed out waiting 120 seconds for simulator to boot, current state is 1.'
	//  in case the compilation takes a long time.
	// Related Radar link: https://openradar.appspot.com/22413115
	// Demonstration project: https://github.com/bitrise-io/simulator-launch-timeout-includes-build-time
	args = append(args, "build", "test", "-destination", deviceDestination)
	// args = append(args, "build", "test", "-destination", deviceDestination, "-sdk", "iphonesimulator")
	cmd := exec.Command("xcodebuild", args...)

	var outBuffer bytes.Buffer
	var outWriter io.Writer
	if isFullOutputMode {
		outWriter = io.MultiWriter(&outBuffer, os.Stdout)
	} else {
		outWriter = &outBuffer
	}

	cmd.Stdin = nil
	cmd.Stdout = outWriter
	cmd.Stderr = outWriter

	fmt.Println()
	log.Println("=> Compiling and running the tests...")
	cmdArgsForPrint := printableCommandArgs(cmd.Args)

	log.Printf("==> Full command: %s", cmdArgsForPrint)
	if !isFullOutputMode {
		fmt.Println()
		log.Println("=> You selected to only see test results.")
		log.Println("   This can take some time, especially if the code have to be compiled first.")
	}

	runErr := cmd.Run()
	fullOutputStr := outBuffer.String()
	if runErr != nil {
		if isStringFoundInOutput(timeOutMessageIPhoneSimulator, fullOutputStr) {
			log.Println("=> Simulator Timeout detected")
			if isRetryOnTimeout {
				log.Println("==> isRetryOnTimeout=true - retrying...")
				return runTest(action, projectPath, scheme, false, deviceDestination, false, isFullOutputMode)
			}
			log.Println(" [!] isRetryOnTimeout=false, no more retry, stopping the test!")
			return fullOutputStr, runErr
		}

		if isStringFoundInOutput(timeOutMessageUITest, fullOutputStr) {
			log.Println("=> Simulator Timeout detected: isUITestTimeoutFound")
			if isRetryOnTimeout {
				log.Println("==> isRetryOnTimeout=true - retrying...")
				return runTest(action, projectPath, scheme, false, deviceDestination, false, isFullOutputMode)
			}
			log.Println(" [!] isRetryOnTimeout=false, no more retry, stopping the test!")
			return fullOutputStr, runErr
		}

		return fullOutputStr, runErr
	}

	return fullOutputStr, nil
}

func main() {
	//
	// Required parameters
	projectPath, err := validateRequiredInput("project_path")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	scheme, err := validateRequiredInput("scheme")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	simulatorDevice, err := validateRequiredInput("simulator_device")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	simulatorOsVersion, err := validateRequiredInput("simulator_os_version")
	if err != nil {
		log.Fatalf("Input validation failed, err: %s", err)
	}

	//
	// Not required parameters
	cleanBuild := false
	if os.Getenv("is_clean_build") == "yes" {
		cleanBuild = true
	}
	isFullOutputMode := !(os.Getenv("is_full_output") == "no")

	//
	// Project-or-Workspace flag
	action := ""
	if strings.HasSuffix(projectPath, ".xcodeproj") {
		action = "-project"
	} else if strings.HasSuffix(projectPath, ".xcworkspace") {
		action = "-workspace"
	} else {
		log.Fatalf("Failed to get valid project file (invalid project file): %s", projectPath)
	}

	//
	// Device Destination
	// xcodebuild -project ./BitriseSampleWithYML.xcodeproj -scheme BitriseSampleWithYML  test -destination "platform=iOS Simulator,name=iPhone 6 Plus,OS=latest" -sdk iphonesimulator -verbose
	deviceDestination := fmt.Sprintf("platform=iOS Simulator,name=%s,OS=%s", simulatorDevice, simulatorOsVersion)

	//
	// Print configs
	printConfig(projectPath, scheme, simulatorDevice, simulatorOsVersion, action, deviceDestination, cleanBuild)

	//
	// Run
	fullOutputStr, runErr := runTest(action, projectPath, scheme, cleanBuild, deviceDestination, true, isFullOutputMode)

	//
	isRunSuccess := (runErr == nil)
	if isRunSuccess {
		exportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "succeeded")
	} else {
		exportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed")
	}
	testResultsSummary := findTestSummaryInOutput(fullOutputStr, isRunSuccess)
	if testResultsSummary == "" {
		testResultsSummary = fmt.Sprintf(" [!] No test summary found in the output - most likely it was a compilation error.\n\n Full output was: %s", fullOutputStr)
	}
	exportEnvironmentWithEnvman("BITRISE_XCODE_TEST_FULL_RESULTS_TEXT", testResultsSummary)

	if !isFullOutputMode {
		fmt.Println()
		fmt.Println("========= TEST RESULTS: =========")
		fmt.Println(testResultsSummary)
		fmt.Println()
	}

	if runErr != nil {
		log.Fatalf("xcode test failed with error: %s", runErr)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
)

func main() {

	flag.Parse()
	if flag.NArg() < 3 {
		fmt.Println("Please specifiy: index input output")
		return
	}

	// TODO A no write, stdout only option is necessary for sanity

	// Read and build the master replace index
	indexFile, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
		return
	}
	defer indexFile.Close()

	masterIndex, err := newReplaceIndex(indexFile)
	if err != nil {
		switch err {
		case io.EOF:
			fmt.Printf("The index file is empty, can't continue.\n")
			return
		default:
			log.Fatal(err)
			return
		}
	}

	// Read in the input file
	inputFile, err := os.Open(flag.Arg(1))
	if err != nil {
		log.Fatal(err)
		return
	}
	defer inputFile.Close()

	inputData, err := ioutil.ReadAll(inputFile)
	if err != nil {
		log.Fatal(err)
		return
	}

	var off, end int
	inputChan := make(chan line)

	// Send line at a time as work task to generate deltas
	go func() {
		for {
			i := bytes.IndexByte(inputData[off:], '\n')
			end = off + i + 1
			if i < 0 {
				err = io.EOF
				break
			}
			inputChan <- line{off, inputData[off:end]}
			off = end
		}
		close(inputChan)
	}()

	// Collect the resultant deltas
	deltas := producer(inputChan, &masterIndex)

	// for each delta process the corresponding line in inputData

	for i, d := range deltas {
		fmt.Printf("Delta %v: %v\n", i, d)
	}
	//	err := writeNewFile(inputData, deltas, flag.Arg(1))
	//	if err != nil {
	//		log.Fatal(err)
	//		return
	//	}

	//	out := csv.NewWriter(os.Stdout)
	//	out.Write(headerRow)
	//	out.Flush()

}
<commit_msg>It works! Albeit a little crude. Requires further testing & refinement.<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
)

func main() {

	flag.Parse()
	if flag.NArg() < 3 {
		fmt.Println("Please specifiy: index input output")
		return
	}

	// TODO A no write, stdout only option is necessary for sanity

	// Read and build the master replace index
	indexFile, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
		return
	}
	defer indexFile.Close()

	masterIndex, err := newReplaceIndex(indexFile)
	if err != nil {
		switch err {
		case io.EOF:
			fmt.Printf("The index file is empty, can't continue.\n")
			return
		default:
			log.Fatal(err)
			return
		}
	}

	// Read in the input file
	inputFile, err := os.Open(flag.Arg(1))
	if err != nil {
		log.Fatal(err)
		return
	}
	defer inputFile.Close()

	inputData, err := ioutil.ReadAll(inputFile)
	if err != nil {
		log.Fatal(err)
		return
	}

	var off, end int
	inputChan := make(chan line)

	// Send line at a time as work task to generate deltas
	go func() {
		for {
			i := bytes.IndexByte(inputData[off:], '\n')
			end = off + i + 1
			if i < 0 {
				err = io.EOF
				break
			}
			inputChan <- line{off, inputData[off:end]}
			off = end
		}
		close(inputChan)
	}()

	// Collect the resultant deltas
	deltas := producer(inputChan, &masterIndex)

	if len(deltas) == 0 {
		fmt.Printf("No matches found\n")
		return
	}

	// open output file
	outFile, err := os.Create(flag.Arg(2))
	if err != nil {
		panic(err)
	}

	defer func() {
		if err := outFile.Close(); err != nil {
			panic(err)
		}
	}()

	// make a write buffer
	w := bufio.NewWriter(outFile)

	var o int
	// for each delta process the corresponding line in inputData
	for _, d := range deltas {

		// TODO This approach could lead to large slices in memory

		// read up to offset as slice, and write
		_, err = w.Write(inputData[o:d.off])
		if err != nil {
			log.Fatal(err)
			return
		}

		// Write replacement value
		_, err = w.Write(masterIndex.readItem(d.index).replace)
		if err != nil {
			log.Fatal(err)
			return
		}

		o = d.off + len(masterIndex.readItem(d.index).find)
		w.Flush()

	}

	// write out remainder of slice
	_, err = w.Write(inputData[o:])
	if err != nil {
		log.Fatal(err)
		return
	}
	w.Flush()
}
<|endoftext|>