<commit_before>package cache

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/gin-contrib/cache/persistence"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

func init() {
	gin.SetMode(gin.TestMode)
}

func TestCache(t *testing.T) {
	//TODO:unit test
}

func TestWrite(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	store := persistence.NewInMemoryStore(60 * time.Second)
	writer := newCachedWriter(store, time.Second*3, c.Writer, "mykey")
	c.Writer = writer

	c.Writer.WriteHeader(204)
	c.Writer.WriteHeaderNow()
	c.Writer.Write([]byte("foo"))
	assert.Equal(t, 204, c.Writer.Status())
	assert.Equal(t, "foo", w.Body.String())
	assert.True(t, c.Writer.Written())
}

func TestCachePage(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageExpire(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePage(store, time.Second, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	time.Sleep(time.Second * 2)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageAtomic(t *testing.T) {
	// memoryDelayStore is a wrapper of a InMemoryStore
	// designed to simulate data race (by doing a delayed write)
	store := newDelayStore(60 * time.Second)

	router := gin.New()
	router.GET("/atomic", CachePageAtomic(store, time.Second*5, func(c *gin.Context) {
		c.String(200, "OK")
	}))

	outp := make(chan string, 10)

	for i := 0; i < 5; i++ {
		go func() {
			resp := performRequest("GET", "/atomic", router)
			outp <- resp.Body.String()
		}()
	}
	time.Sleep(time.Millisecond * 500)
	for i := 0; i < 5; i++ {
		go func() {
			resp := performRequest("GET", "/atomic", router)
			outp <- resp.Body.String()
		}()
	}
	time.Sleep(time.Millisecond * 500)

	for i := 0; i < 10; i++ {
		v := <-outp
		assert.Equal(t, "OK", v)
	}
}

func TestCachePageWithoutHeader(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePageWithoutHeader(store, time.Second*3, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.Nil(t, w2.Header()["Content-Type"])
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutHeaderExpire(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePage(store, time.Second, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	time.Sleep(time.Second * 2)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.NotNil(t, w2.Header()["Content-Type"])
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCacheHtmlFile(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.LoadHTMLFiles("example/template.html")
	router.GET("/cache_html", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.HTML(http.StatusOK, "template.html", gin.H{"values": fmt.Sprint(time.Now().UnixNano())})
	}))

	w1 := performRequest("GET", "/cache_html", router)
	w2 := performRequest("GET", "/cache_html", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCacheHtmlFileExpire(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.LoadHTMLFiles("example/template.html")
	router.GET("/cache_html", CachePage(store, time.Second*1, func(c *gin.Context) {
		c.HTML(http.StatusOK, "template.html", gin.H{"values": fmt.Sprint(time.Now().UnixNano())})
	}))

	w1 := performRequest("GET", "/cache_html", router)
	time.Sleep(time.Second * 2)
	w2 := performRequest("GET", "/cache_html", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageAborted(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_aborted", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.AbortWithStatusJSON(200, map[string]int64{"time": time.Now().UnixNano()})
	}))

	w1 := performRequest("GET", "/cache_aborted", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_aborted", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePage400(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_400", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(400, fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_400", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_400", router)

	assert.Equal(t, 400, w1.Code)
	assert.Equal(t, 400, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutHeaderAborted(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_aborted", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.AbortWithStatusJSON(200, map[string]int64{"time": time.Now().UnixNano()})
	}))

	w1 := performRequest("GET", "/cache_aborted", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_aborted", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.NotNil(t, w2.Header()["Content-Type"])
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutHeader400(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_400", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(400, fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_400", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_400", router)

	assert.Equal(t, 400, w1.Code)
	assert.Equal(t, 400, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.NotNil(t, w2.Header()["Content-Type"])
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageStatus207(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_207", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(207, fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_207", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_207", router)

	assert.Equal(t, 207, w1.Code)
	assert.Equal(t, 207, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutQuery(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_without_query", CachePageWithoutQuery(store, time.Second*3, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_without_query?foo=1", router)
	w2 := performRequest("GET", "/cache_without_query?foo=2", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func performRequest(method, target string, router *gin.Engine) *httptest.ResponseRecorder {
	r := httptest.NewRequest(method, target, nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	return w
}

type memoryDelayStore struct {
	*persistence.InMemoryStore
}

func newDelayStore(defaultExpiration time.Duration) *memoryDelayStore {
	v := &memoryDelayStore{}
	v.InMemoryStore = persistence.NewInMemoryStore(defaultExpiration)
	return v
}

func (c *memoryDelayStore) Set(key string, value interface{}, expires time.Duration) error {
	time.Sleep(time.Millisecond * 3)
	return c.InMemoryStore.Set(key, value, expires)
}

func (c *memoryDelayStore) Add(key string, value interface{}, expires time.Duration) error {
	time.Sleep(time.Millisecond * 3)
	return c.InMemoryStore.Add(key, value, expires)
}
<commit_msg>new test for RegisterResponseCacheGob()<commit_after>package cache

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
	"encoding/gob"
	"bytes"

	"github.com/gin-contrib/cache/persistence"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

func init() {
	gin.SetMode(gin.TestMode)
}

func TestCache(t *testing.T) {
	//TODO:unit test
}

func TestWrite(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	store := persistence.NewInMemoryStore(60 * time.Second)
	writer := newCachedWriter(store, time.Second*3, c.Writer, "mykey")
	c.Writer = writer

	c.Writer.WriteHeader(204)
	c.Writer.WriteHeaderNow()
	c.Writer.Write([]byte("foo"))
	assert.Equal(t, 204, c.Writer.Status())
	assert.Equal(t, "foo", w.Body.String())
	assert.True(t, c.Writer.Written())
}

func TestCachePage(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageExpire(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePage(store, time.Second, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	time.Sleep(time.Second * 2)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageAtomic(t *testing.T) {
	// memoryDelayStore is a wrapper of a InMemoryStore
	// designed to simulate data race (by doing a delayed write)
	store := newDelayStore(60 * time.Second)

	router := gin.New()
	router.GET("/atomic", CachePageAtomic(store, time.Second*5, func(c *gin.Context) {
		c.String(200, "OK")
	}))

	outp := make(chan string, 10)

	for i := 0; i < 5; i++ {
		go func() {
			resp := performRequest("GET", "/atomic", router)
			outp <- resp.Body.String()
		}()
	}
	time.Sleep(time.Millisecond * 500)
	for i := 0; i < 5; i++ {
		go func() {
			resp := performRequest("GET", "/atomic", router)
			outp <- resp.Body.String()
		}()
	}
	time.Sleep(time.Millisecond * 500)

	for i := 0; i < 10; i++ {
		v := <-outp
		assert.Equal(t, "OK", v)
	}
}

func TestCachePageWithoutHeader(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePageWithoutHeader(store, time.Second*3, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.Nil(t, w2.Header()["Content-Type"])
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutHeaderExpire(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_ping", CachePage(store, time.Second, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_ping", router)
	time.Sleep(time.Second * 2)
	w2 := performRequest("GET", "/cache_ping", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.NotNil(t, w2.Header()["Content-Type"])
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCacheHtmlFile(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.LoadHTMLFiles("example/template.html")
	router.GET("/cache_html", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.HTML(http.StatusOK, "template.html", gin.H{"values": fmt.Sprint(time.Now().UnixNano())})
	}))

	w1 := performRequest("GET", "/cache_html", router)
	w2 := performRequest("GET", "/cache_html", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCacheHtmlFileExpire(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.LoadHTMLFiles("example/template.html")
	router.GET("/cache_html", CachePage(store, time.Second*1, func(c *gin.Context) {
		c.HTML(http.StatusOK, "template.html", gin.H{"values": fmt.Sprint(time.Now().UnixNano())})
	}))

	w1 := performRequest("GET", "/cache_html", router)
	time.Sleep(time.Second * 2)
	w2 := performRequest("GET", "/cache_html", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageAborted(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_aborted", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.AbortWithStatusJSON(200, map[string]int64{"time": time.Now().UnixNano()})
	}))

	w1 := performRequest("GET", "/cache_aborted", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_aborted", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePage400(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_400", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(400, fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_400", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_400", router)

	assert.Equal(t, 400, w1.Code)
	assert.Equal(t, 400, w2.Code)
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutHeaderAborted(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_aborted", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.AbortWithStatusJSON(200, map[string]int64{"time": time.Now().UnixNano()})
	}))

	w1 := performRequest("GET", "/cache_aborted", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_aborted", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.NotNil(t, w2.Header()["Content-Type"])
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutHeader400(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_400", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(400, fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_400", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_400", router)

	assert.Equal(t, 400, w1.Code)
	assert.Equal(t, 400, w2.Code)
	assert.NotNil(t, w1.Header()["Content-Type"])
	assert.NotNil(t, w2.Header()["Content-Type"])
	assert.NotEqual(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageStatus207(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_207", CachePage(store, time.Second*3, func(c *gin.Context) {
		c.String(207, fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_207", router)
	time.Sleep(time.Millisecond * 500)
	w2 := performRequest("GET", "/cache_207", router)

	assert.Equal(t, 207, w1.Code)
	assert.Equal(t, 207, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestCachePageWithoutQuery(t *testing.T) {
	store := persistence.NewInMemoryStore(60 * time.Second)

	router := gin.New()
	router.GET("/cache_without_query", CachePageWithoutQuery(store, time.Second*3, func(c *gin.Context) {
		c.String(200, "pong "+fmt.Sprint(time.Now().UnixNano()))
	}))

	w1 := performRequest("GET", "/cache_without_query?foo=1", router)
	w2 := performRequest("GET", "/cache_without_query?foo=2", router)

	assert.Equal(t, 200, w1.Code)
	assert.Equal(t, 200, w2.Code)
	assert.Equal(t, w1.Body.String(), w2.Body.String())
}

func TestRegisterResponseCacheGob(t *testing.T) {
	RegisterResponseCacheGob()
	r := responseCache{Status:200, Data: []byte("test"),}
	mCache := new(bytes.Buffer)
	encCache := gob.NewEncoder(mCache)
	err := encCache.Encode(r)
	assert.Nil(t, err)

	var decodedResp responseCache
	pCache := bytes.NewBuffer(mCache.Bytes())
	decCache := gob.NewDecoder(pCache)
	err = decCache.Decode(&decodedResp)
	assert.Nil(t,err)

}
func performRequest(method, target string, router *gin.Engine) *httptest.ResponseRecorder {
	r := httptest.NewRequest(method, target, nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	return w
}

type memoryDelayStore struct {
	*persistence.InMemoryStore
}

func newDelayStore(defaultExpiration time.Duration) *memoryDelayStore {
	v := &memoryDelayStore{}
	v.InMemoryStore = persistence.NewInMemoryStore(defaultExpiration)
	return v
}

func (c *memoryDelayStore) Set(key string, value interface{}, expires time.Duration) error {
	time.Sleep(time.Millisecond * 3)
	return c.InMemoryStore.Set(key, value, expires)
}

func (c *memoryDelayStore) Add(key string, value interface{}, expires time.Duration) error {
	time.Sleep(time.Millisecond * 3)
	return c.InMemoryStore.Add(key, value, expires)
}
<|endoftext|>


<commit_before>package ttlcache

import (
	"sync"
	"testing"
	"time"
)

const min = 1 * time.Minute

func TestCaching(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	val := cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined at start")
	}

	cache.Set("foo", 123, min)

	val = cache.Get("foo")
	if val != 123 {
		t.Error("val should be 123 but is ", val)
	}

	cache.Delete("foo")
	val = cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined after deletion")
	}

	val, err := cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
		return "new value", nil
	})
	if err != nil {
		t.Error(err)
	}
	if val != "new value" {
		t.Error("val should be 'new value' but is ", val)
	}

	val, err = cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
		t.Error("producer ran again")
		return "wrong value", nil
	})
	if err != nil {
		t.Error(err)
	}
	if val != "new value" {
		t.Error("val should be 'new value' but is ", val)
	}
}

func TestUpdateContention(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			time.Sleep(time.Duration(i) * 10 * time.Millisecond)
			val, err := cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
				time.Sleep(200 * time.Millisecond)
				return i, nil
			})
			if err != nil {
				t.Error(err)
			}
			if val != 0 {
				t.Error("val should be 0 but is ", val)
			}

			wg.Done()
		}(i)
	}

	cache.GetOrElseUpdate("unrelated", min, func() (interface{}, error) {
		//this should run concurrently with the go-routines from the loop above if
		// entry level locking works properly
		time.Sleep(100 * time.Millisecond)
		return 0, nil
	})

	wg.Wait()

	val := cache.Get("foo")
	if val != 0 {
		t.Error("val should be 0 but is ", val)
	}
}

func TestNonCaching(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	val, err := cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
		return nil, DoNotCache{123}
	})
	if err != nil {
		t.Error(err)
	}
	if val != 123 {
		t.Error("val should be 123 but is ", val)
	}

	val = cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined")
	}
}

func TestCleaner(t *testing.T) {
	cache := NewTtlCache(500 * time.Millisecond)
	defer cache.Close()

	if _, ok := cache.cache["foo"]; ok {
		t.Error("val should not be defined")
	}

	go func() {
		cache.GetOrElseUpdate("foo", 250*time.Millisecond, func() (interface{}, error) {
			time.Sleep(1000 * time.Millisecond)
			return "bar", nil
		})
	}()

	time.Sleep(80 * time.Millisecond)

	if _, ok := cache.cache["foo"]; !ok {
		t.Error("val should be defined")
	}

	time.Sleep(700 * time.Millisecond)

	if _, ok := cache.cache["foo"]; !ok {
		t.Error("val should be defined")
	}

	time.Sleep(1 * time.Second)

	if _, ok := cache.cache["foo"]; ok {
		t.Error("val should not be defined")
	}
}

func TestDisabledCleaner(t *testing.T) {
	cache := NewTtlCache(0)
	defer cache.Close()

	cache.Set("foo", 123, 0)

	val := cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined")
	}
}
<commit_msg>Zero ttl test<commit_after>package ttlcache

import (
	"sync"
	"testing"
	"time"
)

const min = 1 * time.Minute

func TestCaching(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	val := cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined at start")
	}

	cache.Set("foo", 123, min)

	val = cache.Get("foo")
	if val != 123 {
		t.Error("val should be 123 but is ", val)
	}

	cache.Delete("foo")
	val = cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined after deletion")
	}

	val, err := cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
		return "new value", nil
	})
	if err != nil {
		t.Error(err)
	}
	if val != "new value" {
		t.Error("val should be 'new value' but is ", val)
	}

	val, err = cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
		t.Error("producer ran again")
		return "wrong value", nil
	})
	if err != nil {
		t.Error(err)
	}
	if val != "new value" {
		t.Error("val should be 'new value' but is ", val)
	}
}

func TestDisabledCaching(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	val := cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined at start")
	}

	cache.Set("foo", 123, 0)

	val = cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined")
	}

	cache.Delete("foo")
	val = cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined after deletion")
	}

	val, err := cache.GetOrElseUpdate("foo", 0, func() (interface{}, error) {
		return "new value", nil
	})
	if err != nil {
		t.Error(err)
	}
	if val != "new value" {
		t.Error("val should be 'new value' but is ", val)
	}

	called := false

	val, err = cache.GetOrElseUpdate("foo", 0, func() (interface{}, error) {
		called = true
		return "new value", nil
	})
	if err != nil {
		t.Error(err)
	}
	if val != "new value" {
		t.Error("val should be 'new value' but is ", val)
	}
	if !called {
		t.Error("producer was not run again")
	}
}

func TestUpdateContention(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			time.Sleep(time.Duration(i) * 10 * time.Millisecond)
			val, err := cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
				time.Sleep(200 * time.Millisecond)
				return i, nil
			})
			if err != nil {
				t.Error(err)
			}
			if val != 0 {
				t.Error("val should be 0 but is ", val)
			}

			wg.Done()
		}(i)
	}

	cache.GetOrElseUpdate("unrelated", min, func() (interface{}, error) {
		//this should run concurrently with the go-routines from the loop above if
		// entry level locking works properly
		time.Sleep(100 * time.Millisecond)
		return 0, nil
	})

	wg.Wait()

	val := cache.Get("foo")
	if val != 0 {
		t.Error("val should be 0 but is ", val)
	}
}

func TestNonCaching(t *testing.T) {
	cache := NewTtlCache(min)
	defer cache.Close()

	val, err := cache.GetOrElseUpdate("foo", min, func() (interface{}, error) {
		return nil, DoNotCache{123}
	})
	if err != nil {
		t.Error(err)
	}
	if val != 123 {
		t.Error("val should be 123 but is ", val)
	}

	val = cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined")
	}
}

func TestCleaner(t *testing.T) {
	cache := NewTtlCache(500 * time.Millisecond)
	defer cache.Close()

	if _, ok := cache.cache["foo"]; ok {
		t.Error("val should not be defined")
	}

	go func() {
		cache.GetOrElseUpdate("foo", 250*time.Millisecond, func() (interface{}, error) {
			time.Sleep(1000 * time.Millisecond)
			return "bar", nil
		})
	}()

	time.Sleep(80 * time.Millisecond)

	if _, ok := cache.cache["foo"]; !ok {
		t.Error("val should be defined")
	}

	time.Sleep(700 * time.Millisecond)

	if _, ok := cache.cache["foo"]; !ok {
		t.Error("val should be defined")
	}

	time.Sleep(1 * time.Second)

	if _, ok := cache.cache["foo"]; ok {
		t.Error("val should not be defined")
	}
}

func TestDisabledCleaner(t *testing.T) {
	cache := NewTtlCache(0)
	defer cache.Close()

	cache.Set("foo", 123, 0)

	val := cache.Get("foo")
	if val != nil {
		t.Error("val should not be defined")
	}
}
<|endoftext|>


<commit_before>package cfg

import (
	"github.com/hectane/hectane/api"
	"github.com/hectane/hectane/queue"

	"encoding/json"
	"flag"
	"os"
	"path"
)

// Global configuration for the application.
type Config struct {
	API   api.Config   `json:"api"`
	Queue queue.Config `json:"queue"`
}

// Parse the flags passed to the application
func Parse() (*Config, error) {
	var (
		cfg      = &Config{}
		filename = flag.String("config", "", "file containing configuration")
	)
	flag.StringVar(&cfg.API.Addr, "bind", ":8025", "`address` and port to bind to")
	flag.StringVar(&cfg.API.TLSCert, "tls-cert", "", "certificate `file` for TLS")
	flag.StringVar(&cfg.API.TLSKey, "tls-key", "", "private key `file` for TLS")
	flag.StringVar(&cfg.API.Username, "username", "", "`username` for HTTP basic auth")
	flag.StringVar(&cfg.API.Password, "password", "", "`password` for HTTP basic auth")
	flag.StringVar(&cfg.Queue.Directory, "directory", path.Join(os.TempDir(), "hectane"), "`directory` for persistent storage")
	flag.BoolVar(&cfg.Queue.DisableSSLVerification, "disable-ssl-verification", false, "don't verify SSL certificates")
	flag.Parse()
	if *filename != "" {
		r, err := os.Open(*filename)
		if err != nil {
			return nil, err
		}
		defer r.Close()
		if err = json.NewDecoder(r).Decode(cfg); err != nil {
			return nil, err
		}
	}
	return cfg, nil
}
<commit_msg>Added Config.Save() method.<commit_after>package cfg

import (
	"github.com/hectane/hectane/api"
	"github.com/hectane/hectane/queue"

	"encoding/json"
	"flag"
	"os"
	"path"
)

// Global configuration for the application.
type Config struct {
	API   api.Config   `json:"api"`
	Queue queue.Config `json:"queue"`
}

// Parse the flags passed to the application
func Parse() (*Config, error) {
	var (
		c        = &Config{}
		filename = flag.String("config", "", "file containing configuration")
	)
	flag.StringVar(&c.API.Addr, "bind", ":8025", "`address` and port to bind to")
	flag.StringVar(&c.API.TLSCert, "tls-cert", "", "certificate `file` for TLS")
	flag.StringVar(&c.API.TLSKey, "tls-key", "", "private key `file` for TLS")
	flag.StringVar(&c.API.Username, "username", "", "`username` for HTTP basic auth")
	flag.StringVar(&c.API.Password, "password", "", "`password` for HTTP basic auth")
	flag.StringVar(&c.Queue.Directory, "directory", path.Join(os.TempDir(), "hectane"), "`directory` for persistent storage")
	flag.BoolVar(&c.Queue.DisableSSLVerification, "disable-ssl-verification", false, "don't verify SSL certificates")
	flag.Parse()
	if *filename != "" {
		r, err := os.Open(*filename)
		if err != nil {
			return nil, err
		}
		defer r.Close()
		if err = json.NewDecoder(r).Decode(c); err != nil {
			return nil, err
		}
	}
	return c, nil
}

// Save the configuration to the specified path.
func (c *Config) Save(path string) error {
	w, err := os.Create(path)
	if err != nil {
		return err
	}
	defer w.Close()
	if err := json.NewEncoder(w).Encode(c); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package discovery

import (
	"errors"
	"io"
	"io/ioutil"
	golog "log"
	"net"
	"sync"
	"time"

	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/mdns"
	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"

	"github.com/ipfs/go-ipfs/p2p/host"
	"github.com/ipfs/go-ipfs/p2p/peer"
	u "github.com/ipfs/go-ipfs/util"
)

var log = u.Logger("mdns")

const ServiceTag = "discovery.ipfs.io"

type Service interface {
	io.Closer
	RegisterNotifee(Notifee)
	UnregisterNotifee(Notifee)
}

type Notifee interface {
	HandlePeerFound(peer.PeerInfo)
}

type mdnsService struct {
	server  *mdns.Server
	service *mdns.MDNSService
	host    host.Host

	lk       sync.Mutex
	notifees []Notifee
	interval time.Duration
}

func getDialableListenAddrs(ph host.Host) ([]*net.TCPAddr, error) {
	var out []*net.TCPAddr
	for _, addr := range ph.Addrs() {
		na, err := manet.ToNetAddr(addr)
		if err != nil {
			continue
		}
		tcp, ok := na.(*net.TCPAddr)
		if ok {
			out = append(out, tcp)
		}
	}
	if len(out) == 0 {
		return nil, errors.New("failed to find good external addr from peerhost")
	}
	return out, nil
}

func NewMdnsService(peerhost host.Host, interval time.Duration) (Service, error) {

	// TODO: dont let mdns use logging...
	golog.SetOutput(ioutil.Discard)

	var ipaddrs []net.IP
	port := 4001

	addrs, err := getDialableListenAddrs(peerhost)
	if err != nil {
		log.Warning(err)
	} else {
		port = addrs[0].Port
		for _, a := range addrs {
			ipaddrs = append(ipaddrs, a.IP)
		}
	}

	myid := peerhost.ID().Pretty()

	info := []string{myid}
	service, err := mdns.NewMDNSService(myid, ServiceTag, "", "", port, ipaddrs, info)
	if err != nil {
		return nil, err
	}

	// Create the mDNS server, defer shutdown
	server, err := mdns.NewServer(&mdns.Config{Zone: service})
	if err != nil {
		return nil, err
	}

	s := &mdnsService{
		server:   server,
		service:  service,
		host:     peerhost,
		interval: interval,
	}

	go s.pollForEntries()

	return s, nil
}

func (m *mdnsService) Close() error {
	return m.server.Shutdown()
}

func (m *mdnsService) pollForEntries() {
	ticker := time.NewTicker(m.interval)
	for {
		select {
		case <-ticker.C:
			entriesCh := make(chan *mdns.ServiceEntry, 16)
			go func() {
				for entry := range entriesCh {
					m.handleEntry(entry)
				}
			}()

			qp := mdns.QueryParam{}
			qp.Domain = "local"
			qp.Entries = entriesCh
			qp.Service = ServiceTag
			qp.Timeout = time.Second * 5

			err := mdns.Query(&qp)
			if err != nil {
				log.Error("mdns lookup error: ", err)
			}
			close(entriesCh)
		}
	}
}

func (m *mdnsService) handleEntry(e *mdns.ServiceEntry) {
	mpeer, err := peer.IDB58Decode(e.Info)
	if err != nil {
		log.Warning("Error parsing peer ID from mdns entry: ", err)
		return
	}

	if mpeer == m.host.ID() {
		return
	}

	maddr, err := manet.FromNetAddr(&net.TCPAddr{
		IP:   e.AddrV4,
		Port: e.Port,
	})
	if err != nil {
		log.Warning("Error parsing multiaddr from mdns entry: ", err)
		return
	}

	pi := peer.PeerInfo{
		ID:    mpeer,
		Addrs: []ma.Multiaddr{maddr},
	}

	m.lk.Lock()
	for _, n := range m.notifees {
		n.HandlePeerFound(pi)
	}
	m.lk.Unlock()
}

func (m *mdnsService) RegisterNotifee(n Notifee) {
	m.lk.Lock()
	m.notifees = append(m.notifees, n)
	m.lk.Unlock()
}

func (m *mdnsService) UnregisterNotifee(n Notifee) {
	m.lk.Lock()
	found := -1
	for i, notif := range m.notifees {
		if notif == n {
			found = i
			break
		}
	}
	if found != -1 {
		m.notifees = append(m.notifees[:found], m.notifees[found+1:]...)
	}
	m.lk.Unlock()
}
<commit_msg>mdns: use my fork of hashicorp/mdns<commit_after>package discovery

import (
	"errors"
	"io"
	"io/ioutil"
	golog "log"
	"net"
	"sync"
	"time"

	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cryptix/mdns"
	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"

	"github.com/ipfs/go-ipfs/p2p/host"
	"github.com/ipfs/go-ipfs/p2p/peer"
	u "github.com/ipfs/go-ipfs/util"
)

var log = u.Logger("mdns")

const ServiceTag = "discovery.ipfs.io"

type Service interface {
	io.Closer
	RegisterNotifee(Notifee)
	UnregisterNotifee(Notifee)
}

type Notifee interface {
	HandlePeerFound(peer.PeerInfo)
}

type mdnsService struct {
	server  *mdns.Server
	service *mdns.MDNSService
	host    host.Host

	lk       sync.Mutex
	notifees []Notifee
	interval time.Duration
}

func getDialableListenAddrs(ph host.Host) ([]*net.TCPAddr, error) {
	var out []*net.TCPAddr
	for _, addr := range ph.Addrs() {
		na, err := manet.ToNetAddr(addr)
		if err != nil {
			continue
		}
		tcp, ok := na.(*net.TCPAddr)
		if ok {
			out = append(out, tcp)
		}
	}
	if len(out) == 0 {
		return nil, errors.New("failed to find good external addr from peerhost")
	}
	return out, nil
}

func NewMdnsService(peerhost host.Host, interval time.Duration) (Service, error) {

	// TODO: dont let mdns use logging...
	golog.SetOutput(ioutil.Discard)

	var ipaddrs []net.IP
	port := 4001

	addrs, err := getDialableListenAddrs(peerhost)
	if err != nil {
		log.Warning(err)
	} else {
		port = addrs[0].Port
		for _, a := range addrs {
			ipaddrs = append(ipaddrs, a.IP)
		}
	}

	myid := peerhost.ID().Pretty()

	info := []string{myid}
	service, err := mdns.NewMDNSService(myid, ServiceTag, "", "", port, ipaddrs, info)
	if err != nil {
		return nil, err
	}

	// Create the mDNS server, defer shutdown
	server, err := mdns.NewServer(&mdns.Config{Zone: service})
	if err != nil {
		return nil, err
	}

	s := &mdnsService{
		server:   server,
		service:  service,
		host:     peerhost,
		interval: interval,
	}

	go s.pollForEntries()

	return s, nil
}

func (m *mdnsService) Close() error {
	return m.server.Shutdown()
}

func (m *mdnsService) pollForEntries() {
	ticker := time.NewTicker(m.interval)
	for {
		select {
		case <-ticker.C:
			entriesCh := make(chan *mdns.ServiceEntry, 16)
			go func() {
				for entry := range entriesCh {
					m.handleEntry(entry)
				}
			}()

			qp := mdns.QueryParam{}
			qp.Domain = "local"
			qp.Entries = entriesCh
			qp.Service = ServiceTag
			qp.Timeout = time.Second * 5

			err := mdns.Query(&qp)
			if err != nil {
				log.Error("mdns lookup error: ", err)
			}
			close(entriesCh)
		}
	}
}

func (m *mdnsService) handleEntry(e *mdns.ServiceEntry) {
	mpeer, err := peer.IDB58Decode(e.Info)
	if err != nil {
		log.Warning("Error parsing peer ID from mdns entry: ", err)
		return
	}

	if mpeer == m.host.ID() {
		return
	}

	maddr, err := manet.FromNetAddr(&net.TCPAddr{
		IP:   e.AddrV4,
		Port: e.Port,
	})
	if err != nil {
		log.Warning("Error parsing multiaddr from mdns entry: ", err)
		return
	}

	pi := peer.PeerInfo{
		ID:    mpeer,
		Addrs: []ma.Multiaddr{maddr},
	}

	m.lk.Lock()
	for _, n := range m.notifees {
		n.HandlePeerFound(pi)
	}
	m.lk.Unlock()
}

func (m *mdnsService) RegisterNotifee(n Notifee) {
	m.lk.Lock()
	m.notifees = append(m.notifees, n)
	m.lk.Unlock()
}

func (m *mdnsService) UnregisterNotifee(n Notifee) {
	m.lk.Lock()
	found := -1
	for i, notif := range m.notifees {
		if notif == n {
			found = i
			break
		}
	}
	if found != -1 {
		m.notifees = append(m.notifees[:found], m.notifees[found+1:]...)
	}
	m.lk.Unlock()
}
<|endoftext|>


<commit_before>// Package beta provides algorithms for working with beta distributions.
//
// https://en.wikipedia.org/wiki/Beta_distribution
package beta

import (
	"math"
)

// Self represents a particular distribution from the family.
type Self struct {
	α float64
	β float64
	a float64
	b float64
}

// New returns a beta distribution with α and β on [a, b].
func New(α, β, a, b float64) *Self {
	return &Self{α, β, a, b}
}

// CDF evaluates the CDF of the distribution.
func (s *Self) CDF(points []float64) []float64 {
	values := make([]float64, len(points))

	α, β, k, b := s.α, s.β, s.b-s.a, s.a

	for i, x := range points {
		values[i] = betaInc(α, β, (x-b)/k)
	}

	return values
}

func betaInc(α, β, x float64) float64 {
	// Author: John Burkardt
	// Source: http://people.sc.fsu.edu/~jburkardt/c_src/prob/prob.html

	const (
		max = 1000
		tol = 1.0e-07
	)

	if x <= 0 {
		return 0
	} else if 1 <= x {
		return 1
	}

	αx, βx := x, 1 - x

	var flip bool
	if α < (α+β)*x {
		α, αx, β, βx = β, βx, α, αx
		flip = true
	}

	// https://en.wikipedia.org/wiki/Integration_by_reduction_formulae
	rx := αx
	n := int(β + βx*(α+β))
	if n != 0 {
		rx /= βx
	}

	value := 1.0

	// FIXME: Might not converge. Panic?
	for i, temp, psq, term := 1, β - 1, α + β, 1.0; i <= max; {
		term = term * temp * rx / (α + float64(i))

		value += term
		temp = math.Abs(term)

		if temp <= tol && temp <= tol*value {
			break
		}

		i++
		n--

		if 0 <= n {
			temp = β - float64(i)
			if n == 0 {
				rx = αx
			}
		} else {
			temp = psq
			psq += 1
		}
	}

	// http://dlmf.nist.gov/8.17#v
	value = value * math.Exp(α*math.Log(αx)+(β-1)*math.Log(βx)) / (beta(α, β) * α)

	if flip {
		return 1 - value
	} else {
		return value
	}
}

func beta(x, y float64) float64 {
	z, _ := math.Lgamma(x + y)
	x, _ = math.Lgamma(x)
	y, _ = math.Lgamma(y)

	return math.Exp(x + y - z)
}
<commit_msg>Go fmt!<commit_after>// Package beta provides algorithms for working with beta distributions.
//
// https://en.wikipedia.org/wiki/Beta_distribution
package beta

import (
	"math"
)

// Self represents a particular distribution from the family.
type Self struct {
	α float64
	β float64
	a float64
	b float64
}

// New returns a beta distribution with α and β on [a, b].
func New(α, β, a, b float64) *Self {
	return &Self{α, β, a, b}
}

// CDF evaluates the CDF of the distribution.
func (s *Self) CDF(points []float64) []float64 {
	values := make([]float64, len(points))

	α, β, k, b := s.α, s.β, s.b-s.a, s.a

	for i, x := range points {
		values[i] = betaInc(α, β, (x-b)/k)
	}

	return values
}

func betaInc(α, β, x float64) float64 {
	// Author: John Burkardt
	// Source: http://people.sc.fsu.edu/~jburkardt/c_src/prob/prob.html

	const (
		max = 1000
		tol = 1.0e-07
	)

	if x <= 0 {
		return 0
	} else if 1 <= x {
		return 1
	}

	αx, βx := x, 1-x

	var flip bool
	if α < (α+β)*x {
		α, αx, β, βx = β, βx, α, αx
		flip = true
	}

	// https://en.wikipedia.org/wiki/Integration_by_reduction_formulae
	rx := αx
	n := int(β + βx*(α+β))
	if n != 0 {
		rx /= βx
	}

	value := 1.0

	// FIXME: Might not converge. Panic?
	for i, temp, psq, term := 1, β-1, α+β, 1.0; i <= max; {
		term = term * temp * rx / (α + float64(i))

		value += term
		temp = math.Abs(term)

		if temp <= tol && temp <= tol*value {
			break
		}

		i++
		n--

		if 0 <= n {
			temp = β - float64(i)
			if n == 0 {
				rx = αx
			}
		} else {
			temp = psq
			psq += 1
		}
	}

	// http://dlmf.nist.gov/8.17#v
	value = value * math.Exp(α*math.Log(αx)+(β-1)*math.Log(βx)) / (beta(α, β) * α)

	if flip {
		return 1 - value
	} else {
		return value
	}
}

func beta(x, y float64) float64 {
	z, _ := math.Lgamma(x + y)
	x, _ = math.Lgamma(x)
	y, _ = math.Lgamma(y)

	return math.Exp(x + y - z)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

// Register all the available providers.
import (
	_ "launchpad.net/juju-core/environs/azure"
	_ "launchpad.net/juju-core/environs/ec2"
	_ "launchpad.net/juju-core/environs/maas"
	_ "launchpad.net/juju-core/environs/openstack"
)
<commit_msg>Hook up the local provider.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

// Register all the available providers.
import (
	_ "launchpad.net/juju-core/environs/azure"
	_ "launchpad.net/juju-core/environs/ec2"
	_ "launchpad.net/juju-core/environs/local"
	_ "launchpad.net/juju-core/environs/maas"
	_ "launchpad.net/juju-core/environs/openstack"
)
<|endoftext|>


<commit_before>/*
Released under MIT License <http://www.opensource.org/licenses/mit-license.php
Copyright (C) ITsysCOM GmbH. All Rights Reserved.

Provides Kamailio evapi socket communication.
*/

package kamevapi

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"regexp"
	"strconv"
	"sync"
	"time"
)

// successive Fibonacci numbers.
func fib() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}

// Creates a new kamEvApi, connects it and in case forkRead is enabled starts listening in background
func NewKamEvapi(addr, connId string, recons int, eventHandlers map[*regexp.Regexp][]func([]byte, string), logger *log.Logger) (*KamEvapi, error) {
	kea := &KamEvapi{kamaddr: addr, connId: connId, reconnects: recons, eventHandlers: eventHandlers, logger: logger, delayFunc: fib(), connMutex: new(sync.RWMutex)}
	if err := kea.Connect(); err != nil {
		return nil, err
	}
	return kea, nil
}

type KamEvapi struct {
	kamaddr        string // IP:Port address where to reach kamailio
	connId         string // Optional connection identifier between library and component using it
	reconnects     int
	eventHandlers  map[*regexp.Regexp][]func([]byte, string)
	logger         *log.Logger
	delayFunc      func() int
	conn           net.Conn
	rcvBuffer      *bufio.Reader
	dataInChan     chan string   // Listen here for replies from Kamailio
	stopReadEvents chan struct{} //Keep a reference towards forkedReadEvents so we can stop them whenever necessary
	errReadEvents  chan error
	connMutex      *sync.RWMutex
}

// Reads bytes from the buffer and dispatch content received as netstring
func (kea *KamEvapi) readNetstring() ([]byte, error) {
	contentLenStr, err := kea.rcvBuffer.ReadString(':')
	if err != nil {
		return nil, err
	}
	cntLen, err := strconv.Atoi(contentLenStr[:len(contentLenStr)-1])
	if err != nil {
		return nil, err
	}
	bytesRead := make([]byte, cntLen)
	for i := 0; i < cntLen; i++ {
		byteRead, err := kea.rcvBuffer.ReadByte()
		if err != nil {
			return nil, err
		}
		bytesRead[i] = byteRead
	}
	if byteRead, err := kea.rcvBuffer.ReadByte(); err != nil { // Crosscheck that our received content ends in , which is standard for netstrings
		return nil, err
	} else if byteRead != ',' {
		return nil, fmt.Errorf("Crosschecking netstring failed, no comma in the end but: %s", string(byteRead))
	}
	return bytesRead, nil
}

// Reads netstrings from socket, dispatch content
func (kea *KamEvapi) readEvents(exitChan chan struct{}, errReadEvents chan error) {
	for {
		select {
		case <-exitChan:
			return
		default: // Unlock waiting here
		}
		dataIn, err := kea.readNetstring()
		if err != nil {
			errReadEvents <- err
			return
		}
		kea.dispatchEvent(dataIn)
	}
	return
}

// Formats string content as netstring and sends over the socket
func (kea *KamEvapi) sendAsNetstring(dataStr string) error {
	cntLen := len([]byte(dataStr)) // Netstrings require number of bytes sent
	dataOut := fmt.Sprintf("%d:%s,", cntLen, dataStr)
	kea.connMutex.RLock()
	fmt.Fprint(kea.conn, dataOut)
	kea.connMutex.RUnlock()
	return nil
}

// Dispatch the event received from Kamailio towards handlers matching it
func (kea *KamEvapi) dispatchEvent(dataIn []byte) {
	matched := false
	for matcher, handlers := range kea.eventHandlers {
		if matcher.Match(dataIn) {
			matched = true
			for _, f := range handlers {
				go f(dataIn, kea.connId)
			}
		}
	}
	if !matched {
		kea.logger.Printf(fmt.Sprintf("<KamEvapi> WARNING: No handler for inbound data: %s", dataIn))
	}
}

// Checks if socket connected. Can be extended with pings
func (kea *KamEvapi) Connected() bool {
	kea.connMutex.RLock()
	defer kea.connMutex.RUnlock()
	if kea.conn == nil {
		return false
	}
	return true
}

// Disconnects from socket
func (kea *KamEvapi) Disconnect() (err error) {
	kea.connMutex.Lock()
	defer kea.connMutex.Unlock()
	if kea.conn != nil {
		kea.logger.Printf(fmt.Sprintf("<KamEvapi> Disconnecting from %s", kea.kamaddr))
		err = kea.conn.Close()
		kea.conn = nil
	}
	return
}

// Connect or reconnect
func (kea *KamEvapi) Connect() error {
	if kea.Connected() {
		kea.Disconnect()
	}
	if kea.stopReadEvents != nil { // ToDo: Check if the channel is not already closed
		close(kea.stopReadEvents) // we have read events already processing, request stop
		kea.stopReadEvents = nil
	}
	var err error
	if kea.logger != nil {
		kea.logger.Printf(fmt.Sprintf("<KamEvapi> Attempting connect to Kamailio at: %s", kea.kamaddr))
	}

	conn, err := net.Dial("tcp", kea.kamaddr)
	if err != nil {
		return err
	}
	kea.connMutex.Lock()
	kea.conn = conn
	kea.connMutex.Unlock()
	if kea.logger != nil {
		kea.logger.Printf(fmt.Sprintf("<KamEvapi> Successfully connected to %s!", kea.kamaddr))
	}
	// Connected, init buffer and prepare sync channels
	kea.connMutex.RLock()
	kea.rcvBuffer = bufio.NewReaderSize(kea.conn, 8192) // reinit buffer
	kea.connMutex.RUnlock()
	stopReadEvents := make(chan struct{})
	kea.stopReadEvents = stopReadEvents
	kea.errReadEvents = make(chan error)
	go kea.readEvents(stopReadEvents, kea.errReadEvents) // Fork read events in it's own goroutine
	return nil                                           // Connected
}

// If not connected, attempt reconnect if allowed
func (kea *KamEvapi) ReconnectIfNeeded() error {
	if kea.Connected() { // No need to reconnect
		return nil
	}
	var err error
	for i := 0; i < kea.reconnects; i++ {
		if err = kea.Connect(); err == nil || kea.Connected() {
			break // No error or unrelated to connection
		}
		time.Sleep(time.Duration(kea.delayFunc()) * time.Second)
	}
	i := 0
	for {
		if kea.reconnects != -1 && i >= kea.reconnects { // Maximum reconnects reached, -1 for infinite reconnects
			break
		}
		if err = kea.Connect(); err == nil || kea.Connected() {
			kea.delayFunc = fib() // Reset the reconnect delay
			break                 // No error or unrelated to connection
		}
		time.Sleep(time.Duration(kea.delayFunc()) * time.Second)
		i++
	}
	if err == nil && !kea.Connected() {
		return errors.New("Not connected to Kamailio")
	}
	return err // nil or last error in the loop
}

// Reads events from socket, attempt reconnect if disconnected
func (kea *KamEvapi) ReadEvents() (err error) {
	for {
		if err = <-kea.errReadEvents; err == io.EOF { // Disconnected, try reconnect
			if err = kea.Disconnect(); err != nil {
				break
			}
			if err = kea.ReconnectIfNeeded(); err != nil {
				break
			}
		}
	}
	return err
}

// Send data to Kamailio
func (kea *KamEvapi) Send(dataStr string) error {
	if err := kea.ReconnectIfNeeded(); err != nil {
		return err
	}
	return kea.sendAsNetstring(dataStr)
	//resSend := <-kea.dataInChan
	//return resSend, nil
}

// Connection handler for commands sent to FreeSWITCH
type KamEvapiPool struct {
	kamAddr      string
	connId       string
	reconnects   int
	logger       *log.Logger
	allowedConns chan struct{}  // Will be populated with allowed new connections
	conns        chan *KamEvapi // Keep here reference towards the list of opened sockets
}

// Retrieves a connection from the pool
func (keap *KamEvapiPool) PopKamEvapi() (*KamEvapi, error) {
	if keap == nil {
		return nil, errors.New("UNCONFIGURED_KAMAILIO_POOL")
	}
	if len(keap.conns) != 0 { // Select directly if available, so we avoid randomness of selection
		KamEvapi := <-keap.conns
		return KamEvapi, nil
	}
	var KamEvapi *KamEvapi
	var err error
	select { // No KamEvapi available in the pool, wait for first one showing up
	case KamEvapi = <-keap.conns:
	case <-keap.allowedConns:
		KamEvapi, err = NewKamEvapi(keap.kamAddr, keap.connId, keap.reconnects, nil, keap.logger)
		if err != nil {
			return nil, err
		}
		return KamEvapi, nil
	}
	return KamEvapi, nil
}

// Push the connection back to the pool
func (keap *KamEvapiPool) PushKamEvapi(kea *KamEvapi) {
	if keap == nil { // Did not initialize the pool
		return
	}
	if kea == nil || !kea.Connected() {
		keap.allowedConns <- struct{}{}
		return
	}
	keap.conns <- kea
}

// Instantiates a new KamEvapiPool
func NewKamEvapiPool(maxconns int, kamAddr, connId string, reconnects int, l *log.Logger) (*KamEvapiPool, error) {
	pool := &KamEvapiPool{kamAddr: kamAddr, connId: connId, reconnects: reconnects, logger: l}
	pool.allowedConns = make(chan struct{}, maxconns)
	var emptyConn struct{}
	for i := 0; i < maxconns; i++ {
		pool.allowedConns <- emptyConn // Empty initiate so we do not need to wait later when we pop
	}
	pool.conns = make(chan *KamEvapi, maxconns)
	return pool, nil
}
<commit_msg>modified logs<commit_after>/*
Released under MIT License <http://www.opensource.org/licenses/mit-license.php
Copyright (C) ITsysCOM GmbH. All Rights Reserved.

Provides Kamailio evapi socket communication.
*/

package kamevapi

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"regexp"
	"strconv"
	"sync"
	"time"
)

// successive Fibonacci numbers.
func fib() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}

// Creates a new kamEvApi, connects it and in case forkRead is enabled starts listening in background
func NewKamEvapi(addr, connId string, recons int, eventHandlers map[*regexp.Regexp][]func([]byte, string), logger *log.Logger) (*KamEvapi, error) {
	kea := &KamEvapi{kamaddr: addr, connId: connId, reconnects: recons, eventHandlers: eventHandlers, logger: logger, delayFunc: fib(), connMutex: new(sync.RWMutex)}
	if err := kea.Connect(); err != nil {
		return nil, err
	}
	return kea, nil
}

type KamEvapi struct {
	kamaddr        string // IP:Port address where to reach kamailio
	connId         string // Optional connection identifier between library and component using it
	reconnects     int
	eventHandlers  map[*regexp.Regexp][]func([]byte, string)
	logger         *log.Logger
	delayFunc      func() int
	conn           net.Conn
	rcvBuffer      *bufio.Reader
	dataInChan     chan string   // Listen here for replies from Kamailio
	stopReadEvents chan struct{} //Keep a reference towards forkedReadEvents so we can stop them whenever necessary
	errReadEvents  chan error
	connMutex      *sync.RWMutex
}

// Reads bytes from the buffer and dispatch content received as netstring
func (kea *KamEvapi) readNetstring() ([]byte, error) {
	contentLenStr, err := kea.rcvBuffer.ReadString(':')
	if err != nil {
		return nil, err
	}
	cntLen, err := strconv.Atoi(contentLenStr[:len(contentLenStr)-1])
	if err != nil {
		return nil, err
	}
	bytesRead := make([]byte, cntLen)
	for i := 0; i < cntLen; i++ {
		byteRead, err := kea.rcvBuffer.ReadByte()
		if err != nil {
			return nil, err
		}
		bytesRead[i] = byteRead
	}
	if byteRead, err := kea.rcvBuffer.ReadByte(); err != nil { // Crosscheck that our received content ends in , which is standard for netstrings
		return nil, err
	} else if byteRead != ',' {
		return nil, fmt.Errorf("Crosschecking netstring failed, no comma in the end but: %s", string(byteRead))
	}
	return bytesRead, nil
}

// Reads netstrings from socket, dispatch content
func (kea *KamEvapi) readEvents(exitChan chan struct{}, errReadEvents chan error) {
	for {
		select {
		case <-exitChan:
			return
		default: // Unlock waiting here
		}
		dataIn, err := kea.readNetstring()
		if err != nil {
			errReadEvents <- err
			return
		}
		kea.dispatchEvent(dataIn)
	}
	return
}

// Formats string content as netstring and sends over the socket
func (kea *KamEvapi) sendAsNetstring(dataStr string) error {
	cntLen := len([]byte(dataStr)) // Netstrings require number of bytes sent
	dataOut := fmt.Sprintf("%d:%s,", cntLen, dataStr)
	kea.connMutex.RLock()
	fmt.Fprint(kea.conn, dataOut)
	kea.connMutex.RUnlock()
	return nil
}

// Dispatch the event received from Kamailio towards handlers matching it
func (kea *KamEvapi) dispatchEvent(dataIn []byte) {
	matched := false
	for matcher, handlers := range kea.eventHandlers {
		if matcher.Match(dataIn) {
			matched = true
			for _, f := range handlers {
				go f(dataIn, kea.connId)
			}
		}
	}
	if !matched {
		kea.logger.Printf(fmt.Sprintf("WARNING: No handler for inbound data: %s", dataIn))
	}
}

// Checks if socket connected. Can be extended with pings
func (kea *KamEvapi) Connected() bool {
	kea.connMutex.RLock()
	defer kea.connMutex.RUnlock()
	if kea.conn == nil {
		return false
	}
	return true
}

// Disconnects from socket
func (kea *KamEvapi) Disconnect() (err error) {
	kea.connMutex.Lock()
	defer kea.connMutex.Unlock()
	if kea.conn != nil {
		kea.logger.Printf(fmt.Sprintf(" Disconnecting from %s", kea.kamaddr))
		err = kea.conn.Close()
		kea.conn = nil
	}
	return
}

// Connect or reconnect
func (kea *KamEvapi) Connect() error {
	if kea.Connected() {
		kea.Disconnect()
	}
	if kea.stopReadEvents != nil { // ToDo: Check if the channel is not already closed
		close(kea.stopReadEvents) // we have read events already processing, request stop
		kea.stopReadEvents = nil
	}
	var err error
	if kea.logger != nil {
		kea.logger.Printf(fmt.Sprintf(" Attempting connect to Kamailio at: %s", kea.kamaddr))
	}

	conn, err := net.Dial("tcp", kea.kamaddr)
	if err != nil {
		return err
	}
	kea.connMutex.Lock()
	kea.conn = conn
	kea.connMutex.Unlock()
	if kea.logger != nil {
		kea.logger.Printf(fmt.Sprintf(" Successfully connected to %s!", kea.kamaddr))
	}
	// Connected, init buffer and prepare sync channels
	kea.connMutex.RLock()
	kea.rcvBuffer = bufio.NewReaderSize(kea.conn, 8192) // reinit buffer
	kea.connMutex.RUnlock()
	stopReadEvents := make(chan struct{})
	kea.stopReadEvents = stopReadEvents
	kea.errReadEvents = make(chan error)
	go kea.readEvents(stopReadEvents, kea.errReadEvents) // Fork read events in it's own goroutine
	return nil                                           // Connected
}

// If not connected, attempt reconnect if allowed
func (kea *KamEvapi) ReconnectIfNeeded() error {
	if kea.Connected() { // No need to reconnect
		return nil
	}
	var err error
	for i := 0; i < kea.reconnects; i++ {
		if err = kea.Connect(); err == nil || kea.Connected() {
			break // No error or unrelated to connection
		}
		time.Sleep(time.Duration(kea.delayFunc()) * time.Second)
	}
	i := 0
	for {
		if kea.reconnects != -1 && i >= kea.reconnects { // Maximum reconnects reached, -1 for infinite reconnects
			break
		}
		if err = kea.Connect(); err == nil || kea.Connected() {
			kea.delayFunc = fib() // Reset the reconnect delay
			break                 // No error or unrelated to connection
		}
		time.Sleep(time.Duration(kea.delayFunc()) * time.Second)
		i++
	}
	if err == nil && !kea.Connected() {
		return errors.New("Not connected to Kamailio")
	}
	return err // nil or last error in the loop
}

// Reads events from socket, attempt reconnect if disconnected
func (kea *KamEvapi) ReadEvents() (err error) {
	for {
		if err = <-kea.errReadEvents; err == io.EOF { // Disconnected, try reconnect
			if err = kea.Disconnect(); err != nil {
				break
			}
			if err = kea.ReconnectIfNeeded(); err != nil {
				break
			}
		}
	}
	return err
}

// Send data to Kamailio
func (kea *KamEvapi) Send(dataStr string) error {
	if err := kea.ReconnectIfNeeded(); err != nil {
		return err
	}
	return kea.sendAsNetstring(dataStr)
	//resSend := <-kea.dataInChan
	//return resSend, nil
}

// Connection handler for commands sent to FreeSWITCH
type KamEvapiPool struct {
	kamAddr      string
	connId       string
	reconnects   int
	logger       *log.Logger
	allowedConns chan struct{}  // Will be populated with allowed new connections
	conns        chan *KamEvapi // Keep here reference towards the list of opened sockets
}

// Retrieves a connection from the pool
func (keap *KamEvapiPool) PopKamEvapi() (*KamEvapi, error) {
	if keap == nil {
		return nil, errors.New("UNCONFIGURED_KAMAILIO_POOL")
	}
	if len(keap.conns) != 0 { // Select directly if available, so we avoid randomness of selection
		KamEvapi := <-keap.conns
		return KamEvapi, nil
	}
	var KamEvapi *KamEvapi
	var err error
	select { // No KamEvapi available in the pool, wait for first one showing up
	case KamEvapi = <-keap.conns:
	case <-keap.allowedConns:
		KamEvapi, err = NewKamEvapi(keap.kamAddr, keap.connId, keap.reconnects, nil, keap.logger)
		if err != nil {
			return nil, err
		}
		return KamEvapi, nil
	}
	return KamEvapi, nil
}

// Push the connection back to the pool
func (keap *KamEvapiPool) PushKamEvapi(kea *KamEvapi) {
	if keap == nil { // Did not initialize the pool
		return
	}
	if kea == nil || !kea.Connected() {
		keap.allowedConns <- struct{}{}
		return
	}
	keap.conns <- kea
}

// Instantiates a new KamEvapiPool
func NewKamEvapiPool(maxconns int, kamAddr, connId string, reconnects int, l *log.Logger) (*KamEvapiPool, error) {
	pool := &KamEvapiPool{kamAddr: kamAddr, connId: connId, reconnects: reconnects, logger: l}
	pool.allowedConns = make(chan struct{}, maxconns)
	var emptyConn struct{}
	for i := 0; i < maxconns; i++ {
		pool.allowedConns <- emptyConn // Empty initiate so we do not need to wait later when we pop
	}
	pool.conns = make(chan *KamEvapi, maxconns)
	return pool, nil
}
<|endoftext|>


<commit_before>// ! $G $D/$F.go >/dev/null
// # ignoring error messages...

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func main (x, x int) {  // BUG redeclaration error
}
<commit_msg>test: change bug040 to use errorcheck<commit_after>// errorcheck

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func f (x,		// GCCGO_ERROR "previous"
	x int) {	// ERROR "redeclared|redefinition" "duplicate"
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

type T struct {
	// legal according to spec
	x int
	y (int)
	int
	*float
	// not legal according to spec
	(complex)  // ERROR "non-declaration|expected|parenthesize"
	(*string)  // ERROR "non-declaration|expected|parenthesize"
	*(bool)    // ERROR "non-declaration|expected|parenthesize"
}

// legal according to spec
func (p T) m() {}

// not legal according to spec
func (p (T)) f() {}   // ERROR "parenthesize"
func (p *(T)) g() {}  // ERROR "parenthesize"
func (p (*T)) h() {}  // ERROR "parenthesize"
<commit_msg>test: Recognize gccgo error messages.<commit_after>// errchk $G $D/$F.go

// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

type T struct {
	// legal according to spec
	x int
	y (int)
	int
	*float
	// not legal according to spec
	(complex)  // ERROR "non-declaration|expected|parenthesize"
	(*string)  // ERROR "non-declaration|expected|parenthesize"
	*(bool)    // ERROR "non-declaration|expected|parenthesize"
}

// legal according to spec
func (p T) m() {}

// not legal according to spec
func (p (T)) f() {}   // ERROR "parenthesize|expected"
func (p *(T)) g() {}  // ERROR "parenthesize|expected"
func (p (*T)) h() {}  // ERROR "parenthesize|expected"
<|endoftext|>


<commit_before>// Copyright 2015 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.

package flagz

import (
	"fmt"
	"testing"
	"time"

	flag "github.com/spf13/pflag"
	"github.com/stretchr/testify/assert"
)

func TestDynDuration_SetAndGet(t *testing.T) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	dynFlag := DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it")
	assert.Equal(t, 5*time.Second, dynFlag.Get(), "value must be default after create")
	err := set.Set("some_duration_1", "10h")
	assert.NoError(t, err, "setting value must succeed")
	assert.Equal(t, 10*time.Hour, dynFlag.Get(), "value must be set after update")
}

func TestDynDuration_IsMarkedDynamic(t *testing.T) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	DynDuration(set, "some_duration_1", 5*time.Minute, "Use it or lose it")
	assert.True(t, IsFlagDynamic(set.Lookup("some_duration_1")))
}

func TestDynDuration_FiresValidators(t *testing.T) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	validator := func(x time.Duration) error {
		if x > 1*time.Hour {
			return fmt.Errorf("too long")
		}
		return nil
	}
	DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it").WithValidator(validator)

	assert.NoError(t, set.Set("some_duration_1", "50m"), "no error from validator when in range")
	assert.Error(t, set.Set("some_duration_1", "2h"), "error from validator when value out of range")
}

func TestDynDuration_FiresNotifier(t *testing.T) {
	waitCh := make(chan bool, 1)
	notifier := func(oldVal time.Duration, newVal time.Duration) {
		assert.EqualValues(t, 5*time.Second, oldVal, "old value in notify must match previous value")
		assert.EqualValues(t, 30*time.Second, newVal, "new value in notify must match set value")
		waitCh <- true
	}

	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it").WithNotifier(notifier)
	set.Set("some_duration_1", "30s")
	select {
	case <-time.After(5 * time.Millisecond):
		assert.Fail(t, "failed to trigger notifier")
	case <-waitCh:
	}
}

func Benchmark_Duration_Dyn_Get(b *testing.B) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	value := DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it")
	set.Set("some_duration_1", "10s")
	for i := 0; i < b.N; i++ {
		value.Get().Nanoseconds()
	}
}

func Benchmark_Duration_Normal_get(b *testing.B) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	valPtr := set.Duration("some_duration_1", 5*time.Second, "Use it or lose it")
	set.Set("some_duration_1", "10s")
	for i := 0; i < b.N; i++ {
		*valPtr.Nanoseconds()
	}
}
<commit_msg>fixup test<commit_after>// Copyright 2015 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.

package flagz

import (
	"fmt"
	"testing"
	"time"

	flag "github.com/spf13/pflag"
	"github.com/stretchr/testify/assert"
)

func TestDynDuration_SetAndGet(t *testing.T) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	dynFlag := DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it")
	assert.Equal(t, 5*time.Second, dynFlag.Get(), "value must be default after create")
	err := set.Set("some_duration_1", "10h")
	assert.NoError(t, err, "setting value must succeed")
	assert.Equal(t, 10*time.Hour, dynFlag.Get(), "value must be set after update")
}

func TestDynDuration_IsMarkedDynamic(t *testing.T) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	DynDuration(set, "some_duration_1", 5*time.Minute, "Use it or lose it")
	assert.True(t, IsFlagDynamic(set.Lookup("some_duration_1")))
}

func TestDynDuration_FiresValidators(t *testing.T) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	validator := func(x time.Duration) error {
		if x > 1*time.Hour {
			return fmt.Errorf("too long")
		}
		return nil
	}
	DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it").WithValidator(validator)

	assert.NoError(t, set.Set("some_duration_1", "50m"), "no error from validator when in range")
	assert.Error(t, set.Set("some_duration_1", "2h"), "error from validator when value out of range")
}

func TestDynDuration_FiresNotifier(t *testing.T) {
	waitCh := make(chan bool, 1)
	notifier := func(oldVal time.Duration, newVal time.Duration) {
		assert.EqualValues(t, 5*time.Second, oldVal, "old value in notify must match previous value")
		assert.EqualValues(t, 30*time.Second, newVal, "new value in notify must match set value")
		waitCh <- true
	}

	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it").WithNotifier(notifier)
	set.Set("some_duration_1", "30s")
	select {
	case <-time.After(5 * time.Millisecond):
		assert.Fail(t, "failed to trigger notifier")
	case <-waitCh:
	}
}

func Benchmark_Duration_Dyn_Get(b *testing.B) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	value := DynDuration(set, "some_duration_1", 5*time.Second, "Use it or lose it")
	set.Set("some_duration_1", "10s")
	for i := 0; i < b.N; i++ {
		value.Get().Nanoseconds()
	}
}

func Benchmark_Duration_Normal_get(b *testing.B) {
	set := flag.NewFlagSet("foobar", flag.ContinueOnError)
	valPtr := set.Duration("some_duration_1", 5*time.Second, "Use it or lose it")
	set.Set("some_duration_1", "10s")
	for i := 0; i < b.N; i++ {
		(*valPtr).Nanoseconds()
	}
}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"os"
	"strconv"

	"github.com/lestrrat/go-jwx/jwk"
)

var StorePrefix = "aaa-data"

/*
prefix: {{letsencrypt-base}}/aaa-agent/

Per Store instance:
{{email}}/info
	- {{email}}.json -- the registration info
	- {{email}}.jwk  -- the account private key in JWK

{{email}}/domain/{{domain}}/
    - authz.json    -- the authorization result
	- privkey.pem   -- the private key in PEM
	- fullchain.pem -- the cert + intermediates
	- cert.pem      -- the cert only
	- chain.pem     -- the intermediate only
*/

type Store struct {
	email  string
	filer  Filer
	prefix string
}

func NewStore(email string, filer Filer) (*Store, error) {
	if email == "" {
		return nil, errors.New("aaa: email must not be empty")
	}

	if debug, _ := strconv.ParseBool(os.Getenv("AAA_DEBUG")); debug {
		filer = new(OSFiler)
	}

	s := &Store{
		email:  email,
		filer:  filer,
		prefix: StorePrefix,
	}

	return s, nil
}

// LoadKey returns RSA private key in JWK.
func (s *Store) LoadPrivateKey() (jwk.Key, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("info", s.email+".jwk"))
	if err != nil {
		return nil, err
	}

	keyset, err := jwk.Parse(blob)
	if err != nil {
		return nil, err
	}
	if len(keyset.Keys) == 0 {
		return nil, errors.New("aaa: no key found")
	}

	return keyset.Keys[0], nil
}

func (s *Store) LoadPublicKey() (jwk.Key, error) {
	// LE currently supports RS256 only
	privkey, err := s.LoadPrivateKey()
	if err != nil {
		return nil, err
	}

	key, err := privkey.Materialize()
	if err != nil {
		return nil, err
	}

	rsaPrivKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("aaa: key is not *rsa.PrivateKey but %v", privkey)
	}

	// restore its public key from the private key
	return jwk.NewRsaPublicKey(&rsaPrivKey.PublicKey)
}

func (s *Store) SaveKey(privateKey jwk.Key) error {
	blob, err := json.Marshal(privateKey)
	if err != nil {
		return err
	}

	return s.filer.WriteFile(s.joinPrefix("info", s.email+".jwk"), blob)
}

func (s *Store) SaveCertKey(domain string, privateKey jwk.Key) error {
	key, err := privateKey.Materialize()
	if err != nil {
		return err
	}

	rsaPrivKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return fmt.Errorf("aaa: key is not *rsa.PrivateKey but %v", privateKey)
	}

	buf := new(bytes.Buffer)
	if err := pem.Encode(buf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)}); err != nil {
		return err
	}
	return s.filer.WriteFile(s.joinPrefix("domain", domain, "privkey.pem"), buf.Bytes())
}

func (s *Store) LoadCertKey(domain string) (jwk.Key, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("domain", domain, "privkey.jwk"))
	if err != nil {
		return nil, err
	}

	keyset, err := jwk.Parse(blob)
	if err != nil {
		return nil, err
	}
	if len(keyset.Keys) == 0 {
		return nil, errors.New("aaa: no key found")
	}

	return keyset.Keys[0], nil
}

func (s *Store) LoadCert(domain string) (*x509.Certificate, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("domain", domain, "cert.pem"))
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(blob)
	return x509.ParseCertificate(block.Bytes)
}

func (s *Store) SaveCert(domain string, issuerCert, myCert *x509.Certificate) error {
	buf := new(bytes.Buffer)

	for _, f := range []struct {
		Name  string
		Certs []*x509.Certificate
	}{
		{
			Name:  "fullchain.pem",
			Certs: []*x509.Certificate{myCert, issuerCert},
		},
		{
			Name:  "cert.pem",
			Certs: []*x509.Certificate{myCert},
		},
		{
			Name:  "chain.pem",
			Certs: []*x509.Certificate{issuerCert},
		},
	} {
		for _, cert := range f.Certs {
			if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
				return err
			}
		}

		if err := s.filer.WriteFile(s.joinPrefix("domain", domain, f.Name), buf.Bytes()); err != nil {
			return err
		}

		buf.Reset()
	}

	return nil
}

func (s *Store) LoadAuthorization(domain string) (*Authorization, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("domain", domain, "authz.json"))
	if err != nil {
		return nil, err
	}

	authz := &Authorization{}
	if err := json.Unmarshal(blob, authz); err != nil {
		return nil, err
	}

	return authz, nil
}

func (s *Store) SaveAuthorization(authz *Authorization) error {
	blob, err := json.Marshal(authz)
	if err != nil {
		return err
	}

	domain := authz.Identifier.Value
	return s.filer.WriteFile(s.joinPrefix("domain", domain, "authz.json"), blob)
}

func (s *Store) ListDomains() ([]string, error) {
	dirs, err := s.filer.ListDir(s.joinPrefix("domain"))
	if err != nil {
		return nil, err
	}

	domains := make([]string, 0, len(dirs))
	for _, dir := range dirs {
		elem := s.filer.Split(dir)

		// domain is in 4th element
		if len(elem) > 3 {
			domains = append(domains, elem[3])
		}
	}
	return domains, nil
}

func (s *Store) SaveAccount(account *Account) error {
	blob, err := json.Marshal(account)
	if err != nil {
		return err
	}

	return s.filer.WriteFile(s.joinPrefix("info", s.email+".json"), blob)
}

func (s *Store) LoadAccount() (*Account, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("info", s.email+".json"))
	if err != nil {
		return nil, err
	}

	account := &Account{}
	if err := json.Unmarshal(blob, account); err != nil {
		return nil, err
	}

	return account, nil
}

func (s *Store) joinPrefix(fns ...string) string {
	return s.filer.Join(append([]string{s.prefix, s.email}, fns...)...)
}
<commit_msg>agent: Fix to use pem format for private key<commit_after>package agent

import (
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"os"
	"strconv"

	"github.com/lestrrat/go-jwx/jwk"
)

var StorePrefix = "aaa-data"

/*
prefix: {{letsencrypt-base}}/aaa-agent/

Per Store instance:
{{email}}/info
	- {{email}}.json -- the registration info
	- {{email}}.jwk  -- the account private key in JWK

{{email}}/domain/{{domain}}/
    - authz.json    -- the authorization result
	- privkey.pem   -- the private key in PEM
	- fullchain.pem -- the cert + intermediates
	- cert.pem      -- the cert only
	- chain.pem     -- the intermediate only
*/

type Store struct {
	email  string
	filer  Filer
	prefix string
}

func NewStore(email string, filer Filer) (*Store, error) {
	if email == "" {
		return nil, errors.New("aaa: email must not be empty")
	}

	if debug, _ := strconv.ParseBool(os.Getenv("AAA_DEBUG")); debug {
		filer = new(OSFiler)
	}

	s := &Store{
		email:  email,
		filer:  filer,
		prefix: StorePrefix,
	}

	return s, nil
}

// LoadKey returns RSA private key in JWK.
func (s *Store) LoadPrivateKey() (jwk.Key, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("info", s.email+".jwk"))
	if err != nil {
		return nil, err
	}

	keyset, err := jwk.Parse(blob)
	if err != nil {
		return nil, err
	}
	if len(keyset.Keys) == 0 {
		return nil, errors.New("aaa: no key found")
	}

	return keyset.Keys[0], nil
}

func (s *Store) LoadPublicKey() (jwk.Key, error) {
	// LE currently supports RS256 only
	privkey, err := s.LoadPrivateKey()
	if err != nil {
		return nil, err
	}

	key, err := privkey.Materialize()
	if err != nil {
		return nil, err
	}

	rsaPrivKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("aaa: key is not *rsa.PrivateKey but %v", privkey)
	}

	// restore its public key from the private key
	return jwk.NewRsaPublicKey(&rsaPrivKey.PublicKey)
}

func (s *Store) SaveKey(privateKey jwk.Key) error {
	blob, err := json.Marshal(privateKey)
	if err != nil {
		return err
	}

	return s.filer.WriteFile(s.joinPrefix("info", s.email+".jwk"), blob)
}

func (s *Store) SaveCertKey(domain string, privateKey jwk.Key) error {
	key, err := privateKey.Materialize()
	if err != nil {
		return err
	}

	rsaPrivKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return fmt.Errorf("aaa: key is not *rsa.PrivateKey but %v", privateKey)
	}

	buf := new(bytes.Buffer)
	if err := pem.Encode(buf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)}); err != nil {
		return err
	}
	return s.filer.WriteFile(s.joinPrefix("domain", domain, "privkey.pem"), buf.Bytes())
}

func (s *Store) LoadCertKey(domain string) (jwk.Key, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("domain", domain, "privkey.pem"))
	if err != nil {
		return nil, err
	}

	derBlock, _ := pem.Decode(blob)
	key, err := x509.ParsePKCS1PrivateKey(derBlock.Bytes)
	if err != nil {
		return nil, err
	}

	return jwk.NewRsaPrivateKey(key)
}

func (s *Store) LoadCert(domain string) (*x509.Certificate, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("domain", domain, "cert.pem"))
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(blob)
	return x509.ParseCertificate(block.Bytes)
}

func (s *Store) SaveCert(domain string, issuerCert, myCert *x509.Certificate) error {
	buf := new(bytes.Buffer)

	for _, f := range []struct {
		Name  string
		Certs []*x509.Certificate
	}{
		{
			Name:  "fullchain.pem",
			Certs: []*x509.Certificate{myCert, issuerCert},
		},
		{
			Name:  "cert.pem",
			Certs: []*x509.Certificate{myCert},
		},
		{
			Name:  "chain.pem",
			Certs: []*x509.Certificate{issuerCert},
		},
	} {
		for _, cert := range f.Certs {
			if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
				return err
			}
		}

		if err := s.filer.WriteFile(s.joinPrefix("domain", domain, f.Name), buf.Bytes()); err != nil {
			return err
		}

		buf.Reset()
	}

	return nil
}

func (s *Store) LoadAuthorization(domain string) (*Authorization, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("domain", domain, "authz.json"))
	if err != nil {
		return nil, err
	}

	authz := &Authorization{}
	if err := json.Unmarshal(blob, authz); err != nil {
		return nil, err
	}

	return authz, nil
}

func (s *Store) SaveAuthorization(authz *Authorization) error {
	blob, err := json.Marshal(authz)
	if err != nil {
		return err
	}

	domain := authz.Identifier.Value
	return s.filer.WriteFile(s.joinPrefix("domain", domain, "authz.json"), blob)
}

func (s *Store) ListDomains() ([]string, error) {
	dirs, err := s.filer.ListDir(s.joinPrefix("domain"))
	if err != nil {
		return nil, err
	}

	domains := make([]string, 0, len(dirs))
	for _, dir := range dirs {
		elem := s.filer.Split(dir)

		// domain is in 4th element
		if len(elem) > 3 {
			domains = append(domains, elem[3])
		}
	}
	return domains, nil
}

func (s *Store) SaveAccount(account *Account) error {
	blob, err := json.Marshal(account)
	if err != nil {
		return err
	}

	return s.filer.WriteFile(s.joinPrefix("info", s.email+".json"), blob)
}

func (s *Store) LoadAccount() (*Account, error) {
	blob, err := s.filer.ReadFile(s.joinPrefix("info", s.email+".json"))
	if err != nil {
		return nil, err
	}

	account := &Account{}
	if err := json.Unmarshal(blob, account); err != nil {
		return nil, err
	}

	return account, nil
}

func (s *Store) joinPrefix(fns ...string) string {
	return s.filer.Join(append([]string{s.prefix, s.email}, fns...)...)
}
<|endoftext|>


<commit_before>package main

import "github.com/kataras/iris"

func main() {
	app := iris.New()

	// this works as expected now,
	// will handle *all* expect DELETE requests, even if there is no routes.
	app.Get("/action/{p}", h)

	app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
}

func h(ctx iris.Context) {
	ctx.Writef("[%s] %s : Parameter = `%s`", ctx.Method(), ctx.Path(), ctx.Params().Get("p"))
}

func fallbackHandler(ctx iris.Context) {
	if ctx.Method() == iris.MethodDelete {
		ctx.NextOrNotFound()
		return
	}

	ctx.Writef("[%s] %s : From fallback handler", ctx.Method(), ctx.Path())
}
<commit_msg>Add a missing call for `fallbackHandler` in _examples/routing/fallback-handlers/main.go by @ZaniaDeveloper via PR: #911<commit_after>package main

import "github.com/kataras/iris"

func main() {
	app := iris.New()

	// add a fallback handler to process requests that would not be declared in the router.
	app.Fallback(fallbackHandler)

	// this works as expected now,
	// will handle *all* expect DELETE requests, even if there is no routes.
	app.Get("/action/{p}", h)

	app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
}

func h(ctx iris.Context) {
	ctx.Writef("[%s] %s : Parameter = `%s`", ctx.Method(), ctx.Path(), ctx.Params().Get("p"))
}

func fallbackHandler(ctx iris.Context) {
	if ctx.Method() == iris.MethodDelete {
		ctx.NextOrNotFound()
		return
	}

	ctx.Writef("[%s] %s : From fallback handler", ctx.Method(), ctx.Path())
}
<|endoftext|>


<commit_before>// Copyright © 2016 Nagy Károly Gábriel <karasz@jpi.io>
// This file, part of genet, is free and unencumbered software
// released into the public domain.
// For more information, please refer to <http://unlicense.org/>
//

package genetlib

import (
	"fmt"
	"github.com/vishvananda/netlink"
	//	"net"
	"strings"
)

type genetlink struct {
	index    int
	name     string
	linktype string
	flags    string
	mtu      int
	qdisc    string
	state    string
	mode     string
	group    string
	qlen     int
	hwd      string
	brd      string
}

func isLoopback(z netlink.Link) bool {
	linka := z.Attrs()
	// 2 is loopback
	if linka.Flags&(1<<uint(2)) != 0 {
		return true
	}
	return false
}

func fillAttrs(z netlink.Link) genetlink {
	var result genetlink
	linka := z.Attrs()
	result.index = linka.Index
	result.name = linka.Name
	result.flags = strings.Replace(strings.ToUpper(linka.Flags.String()), "|", ",", -1)
	result.mtu = linka.MTU

	qds, _ := netlink.QdiscList(z)
	for _, qd := range qds {
		qda := qd.Attrs()
		if qda.Parent == netlink.HANDLE_ROOT {
			result.qdisc = qd.Type()
			break
		}
	}

	if linka.HardwareAddr != nil {
		result.hwd = linka.HardwareAddr.String()
		result.brd = "ff:ff:ff:ff:ff:ff"
	} else {
		result.hwd = "00:00:00:00:00:00"
		result.brd = "00:00:00:00:00:00"
	}
	// just for similarity with the iproute2 output
	if isLoopback(z) {
		result.linktype = "loopback"
	} else {
		if z.Type() != "device" {
			result.linktype = z.Type()
		} else {
			result.linktype = "ether"
		}
	}
	result.state = "<NOT IMPLEMENTED>"
	result.mode = "<NOT IMPLEMENTED>"
	result.group = "<NOT IMPLEMENTED>"
	result.qlen = 0

	return result
}

func LinkShow() ([]netlink.Link, error) {
	links, err := netlink.LinkList()
	if err != nil {
		return links, err
	}
	for _, link := range links {
		z := fillAttrs(link)
		fmt.Printf("%d: %s: <%s> mtu %d qdisc %s state %s mode %s group %s qlen %d\n\tlink/%s: %s brd: %s\n",
			z.index, z.name, z.flags, z.mtu, z.qdisc, z.state, z.mode, z.group, z.qlen, z.linktype, z.hwd, z.brd)
	}
	return links, nil
}
<commit_msg>genetlib: implemented qlen reporting to iplink<commit_after>// Copyright © 2016 Nagy Károly Gábriel <karasz@jpi.io>
// This file, part of genet, is free and unencumbered software
// released into the public domain.
// For more information, please refer to <http://unlicense.org/>
//

package genetlib

import (
	"fmt"
	"github.com/vishvananda/netlink"
	//	"net"
	"strings"
)

type genetlink struct {
	index    int
	name     string
	linktype string
	flags    string
	mtu      int
	qdisc    string
	state    string
	mode     string
	group    string
	qlen     int
	hwd      string
	brd      string
}

func isLoopback(z netlink.Link) bool {
	linka := z.Attrs()
	// 2 is loopback
	if linka.Flags&(1<<uint(2)) != 0 {
		return true
	}
	return false
}

func fillAttrs(z netlink.Link) genetlink {
	var result genetlink
	linka := z.Attrs()
	result.index = linka.Index
	result.name = linka.Name
	result.flags = strings.Replace(strings.ToUpper(linka.Flags.String()), "|", ",", -1)
	result.mtu = linka.MTU
	result.qlen = linka.TxQLen

	qds, _ := netlink.QdiscList(z)
	for _, qd := range qds {
		qda := qd.Attrs()
		if qda.Parent == netlink.HANDLE_ROOT {
			result.qdisc = qd.Type()
			break
		}
	}

	if linka.HardwareAddr != nil {
		result.hwd = linka.HardwareAddr.String()
		result.brd = "ff:ff:ff:ff:ff:ff"
	} else {
		result.hwd = "00:00:00:00:00:00"
		result.brd = "00:00:00:00:00:00"
	}
	// just for similarity with the iproute2 output
	if isLoopback(z) {
		result.linktype = "loopback"
	} else {
		if z.Type() != "device" {
			result.linktype = z.Type()
		} else {
			result.linktype = "ether"
		}
	}
	result.state = "<NOT IMPLEMENTED>"
	result.mode = "<NOT IMPLEMENTED>"
	result.group = "<NOT IMPLEMENTED>"

	return result
}

func LinkShow() ([]netlink.Link, error) {
	links, err := netlink.LinkList()
	if err != nil {
		return links, err
	}
	for _, link := range links {
		z := fillAttrs(link)
		fmt.Printf("%d: %s: <%s> mtu %d qdisc %s state %s mode %s group %s qlen %d\n\tlink/%s: %s brd: %s\n",
			z.index, z.name, z.flags, z.mtu, z.qdisc, z.state, z.mode, z.group, z.qlen, z.linktype, z.hwd, z.brd)
	}
	return links, nil
}
<|endoftext|>


<commit_before>package chart

import (
	"bytes"
	"math"
	"testing"
	"time"

	"github.com/blendlabs/go-assert"
	"github.com/wcharczuk/go-chart/drawing"
)

func TestChartGetDPI(t *testing.T) {
	assert := assert.New(t)

	unset := Chart{}
	assert.Equal(DefaultDPI, unset.GetDPI())
	assert.Equal(192, unset.GetDPI(192))

	set := Chart{DPI: 128}
	assert.Equal(128, set.GetDPI())
	assert.Equal(128, set.GetDPI(192))
}

func TestChartGetFont(t *testing.T) {
	assert := assert.New(t)

	f, err := GetDefaultFont()
	assert.Nil(err)

	unset := Chart{}
	assert.Nil(unset.GetFont())

	set := Chart{Font: f}
	assert.NotNil(set.GetFont())
}

func TestChartGetWidth(t *testing.T) {
	assert := assert.New(t)

	unset := Chart{}
	assert.Equal(DefaultChartWidth, unset.GetWidth())

	set := Chart{Width: DefaultChartWidth + 10}
	assert.Equal(DefaultChartWidth+10, set.GetWidth())
}

func TestChartGetHeight(t *testing.T) {
	assert := assert.New(t)

	unset := Chart{}
	assert.Equal(DefaultChartHeight, unset.GetHeight())

	set := Chart{Height: DefaultChartHeight + 10}
	assert.Equal(DefaultChartHeight+10, set.GetHeight())
}

func TestChartGetRanges(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
			ContinuousSeries{
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{-2.1, -1.0, 0, 1.0, 2.0},
			},
			ContinuousSeries{
				YAxis:   YAxisSecondary,
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{10.0, 11.0, 12.0, 13.0, 14.0},
			},
		},
	}

	xrange, yrange, yrangeAlt := c.getRanges()
	assert.Equal(-2.0, xrange.Min)
	assert.Equal(5.0, xrange.Max)

	assert.Equal(-2.1, yrange.Min)
	assert.Equal(4.5, yrange.Max)

	assert.Equal(10.0, yrangeAlt.Min)
	assert.Equal(14.0, yrangeAlt.Max)

	cSet := Chart{
		XAxis: XAxis{
			Range: Range{Min: 9.8, Max: 19.8},
		},
		YAxis: YAxis{
			Range: Range{Min: 9.9, Max: 19.9},
		},
		YAxisSecondary: YAxis{
			Range: Range{Min: 9.7, Max: 19.7},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
			ContinuousSeries{
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{-2.1, -1.0, 0, 1.0, 2.0},
			},
			ContinuousSeries{
				YAxis:   YAxisSecondary,
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{10.0, 11.0, 12.0, 13.0, 14.0},
			},
		},
	}

	xr2, yr2, yra2 := cSet.getRanges()
	assert.Equal(9.8, xr2.Min)
	assert.Equal(19.8, xr2.Max)

	assert.Equal(9.9, yr2.Min)
	assert.Equal(19.9, yr2.Max)

	assert.Equal(9.7, yra2.Min)
	assert.Equal(19.7, yra2.Max)
}

func TestChartGetRangesUseTicks(t *testing.T) {
	assert := assert.New(t)

	// this test asserts that ticks should supercede manual ranges when generating the overall ranges.

	c := Chart{
		YAxis: YAxis{
			Ticks: []Tick{
				{0.0, "Zero"},
				{1.0, "1.0"},
				{2.0, "2.0"},
				{3.0, "3.0"},
				{4.0, "4.0"},
				{5.0, "Five"},
			},
			Range: Range{
				Min: -5.0,
				Max: 5.0,
			},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
		},
	}

	xr, yr, yar := c.getRanges()
	assert.Equal(-2.0, xr.Min)
	assert.Equal(2.0, xr.Max)
	assert.Equal(0.0, yr.Min)
	assert.Equal(5.0, yr.Max)
	assert.True(yar.IsZero(), yar.String())
}

func TestChartGetRangesUseUserRanges(t *testing.T) {
	assert := assert.New(t)

	// this test asserts that ticks should supercede manual ranges when generating the overall ranges.

	c := Chart{
		YAxis: YAxis{
			Range: Range{
				Min: -5.0,
				Max: 5.0,
			},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
		},
	}

	xr, yr, yar := c.getRanges()
	assert.Equal(-2.0, xr.Min)
	assert.Equal(2.0, xr.Max)
	assert.Equal(-5.0, yr.Min)
	assert.Equal(5.0, yr.Max)
	assert.True(yar.IsZero(), yar.String())
}

func TestChartGetBackgroundStyle(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Background: Style{
			FillColor: drawing.ColorBlack,
		},
	}

	bs := c.getBackgroundStyle()
	assert.Equal(bs.FillColor.String(), drawing.ColorBlack.String())
}

func TestChartGetCanvasStyle(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Canvas: Style{
			FillColor: drawing.ColorBlack,
		},
	}

	bs := c.getCanvasStyle()
	assert.Equal(bs.FillColor.String(), drawing.ColorBlack.String())
}

func TestChartGetDefaultCanvasBox(t *testing.T) {
	assert := assert.New(t)

	c := Chart{}
	canvasBoxDefault := c.getDefaultCanvasBox()
	assert.False(canvasBoxDefault.IsZero())
	assert.Equal(DefaultBackgroundPadding.Top, canvasBoxDefault.Top)
	assert.Equal(DefaultBackgroundPadding.Left, canvasBoxDefault.Left)
	assert.Equal(c.GetWidth()-DefaultBackgroundPadding.Right, canvasBoxDefault.Right)
	assert.Equal(c.GetHeight()-DefaultBackgroundPadding.Bottom, canvasBoxDefault.Bottom)

	custom := Chart{
		Background: Style{
			Padding: Box{
				Top:    DefaultBackgroundPadding.Top + 1,
				Left:   DefaultBackgroundPadding.Left + 1,
				Right:  DefaultBackgroundPadding.Right + 1,
				Bottom: DefaultBackgroundPadding.Bottom + 1,
			},
		},
	}
	canvasBoxCustom := custom.getDefaultCanvasBox()
	assert.False(canvasBoxCustom.IsZero())
	assert.Equal(DefaultBackgroundPadding.Top+1, canvasBoxCustom.Top)
	assert.Equal(DefaultBackgroundPadding.Left+1, canvasBoxCustom.Left)
	assert.Equal(c.GetWidth()-(DefaultBackgroundPadding.Right+1), canvasBoxCustom.Right)
	assert.Equal(c.GetHeight()-(DefaultBackgroundPadding.Bottom+1), canvasBoxCustom.Bottom)
}

func TestChartGetValueFormatters(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
			ContinuousSeries{
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{-2.1, -1.0, 0, 1.0, 2.0},
			},
			ContinuousSeries{
				YAxis:   YAxisSecondary,
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{10.0, 11.0, 12.0, 13.0, 14.0},
			},
		},
	}

	dxf, dyf, dyaf := c.getValueFormatters()
	assert.NotNil(dxf)
	assert.NotNil(dyf)
	assert.NotNil(dyaf)
}

func TestChartHasAxes(t *testing.T) {
	assert := assert.New(t)

	assert.False(Chart{}.hasAxes())

	x := Chart{
		XAxis: XAxis{
			Style: Style{
				Show: true,
			},
		},
	}
	assert.True(x.hasAxes())

	y := Chart{
		YAxis: YAxis{
			Style: Style{
				Show: true,
			},
		},
	}
	assert.True(y.hasAxes())

	ya := Chart{
		YAxisSecondary: YAxis{
			Style: Style{
				Show: true,
			},
		},
	}
	assert.True(ya.hasAxes())
}

func TestChartGetAxesTicks(t *testing.T) {
	assert := assert.New(t)

	r, err := PNG(1024, 1024)
	assert.Nil(err)

	c := Chart{
		XAxis: XAxis{
			Style: Style{Show: true},
			Range: Range{Min: 9.8, Max: 19.8},
		},
		YAxis: YAxis{
			Style: Style{Show: true},
			Range: Range{Min: 9.9, Max: 19.9},
		},
		YAxisSecondary: YAxis{
			Style: Style{Show: true},
			Range: Range{Min: 9.7, Max: 19.7},
		},
	}
	xr, yr, yar := c.getRanges()

	xt, yt, yat := c.getAxesTicks(r, xr, yr, yar, FloatValueFormatter, FloatValueFormatter, FloatValueFormatter)
	assert.NotEmpty(xt)
	assert.NotEmpty(yt)
	assert.NotEmpty(yat)
}

func TestChartSingleSeries(t *testing.T) {
	assert := assert.New(t)
	now := time.Now()
	c := Chart{
		Title:  "Hello!",
		Width:  1024,
		Height: 400,
		YAxis: YAxis{
			Range: Range{
				Min: 0.0,
				Max: 4.0,
			},
		},
		Series: []Series{
			TimeSeries{
				Name:    "goog",
				XValues: []time.Time{now.AddDate(0, 0, -3), now.AddDate(0, 0, -2), now.AddDate(0, 0, -1)},
				YValues: []float64{1.0, 2.0, 3.0},
			},
		},
	}

	buffer := bytes.NewBuffer([]byte{})
	c.Render(PNG, buffer)
	assert.NotEmpty(buffer.Bytes())
}

func TestChartRegressionBadRanges(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1)},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
		},
	}
	buffer := bytes.NewBuffer([]byte{})
	c.Render(PNG, buffer)
	assert.True(true, "Render needs to finish.")
}

func TestChartRegressionBadRangesByUser(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		YAxis: YAxis{
			Range: Range{
				Min: math.Inf(-1),
				Max: math.Inf(1), // this could really happen? eh.
			},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: Seq(1.0, 10.0),
				YValues: Seq(1.0, 10.0),
			},
		},
	}
	buffer := bytes.NewBuffer([]byte{})
	c.Render(PNG, buffer)
	assert.True(true, "Render needs to finish.")
}
<commit_msg>bad comment.<commit_after>package chart

import (
	"bytes"
	"math"
	"testing"
	"time"

	"github.com/blendlabs/go-assert"
	"github.com/wcharczuk/go-chart/drawing"
)

func TestChartGetDPI(t *testing.T) {
	assert := assert.New(t)

	unset := Chart{}
	assert.Equal(DefaultDPI, unset.GetDPI())
	assert.Equal(192, unset.GetDPI(192))

	set := Chart{DPI: 128}
	assert.Equal(128, set.GetDPI())
	assert.Equal(128, set.GetDPI(192))
}

func TestChartGetFont(t *testing.T) {
	assert := assert.New(t)

	f, err := GetDefaultFont()
	assert.Nil(err)

	unset := Chart{}
	assert.Nil(unset.GetFont())

	set := Chart{Font: f}
	assert.NotNil(set.GetFont())
}

func TestChartGetWidth(t *testing.T) {
	assert := assert.New(t)

	unset := Chart{}
	assert.Equal(DefaultChartWidth, unset.GetWidth())

	set := Chart{Width: DefaultChartWidth + 10}
	assert.Equal(DefaultChartWidth+10, set.GetWidth())
}

func TestChartGetHeight(t *testing.T) {
	assert := assert.New(t)

	unset := Chart{}
	assert.Equal(DefaultChartHeight, unset.GetHeight())

	set := Chart{Height: DefaultChartHeight + 10}
	assert.Equal(DefaultChartHeight+10, set.GetHeight())
}

func TestChartGetRanges(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
			ContinuousSeries{
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{-2.1, -1.0, 0, 1.0, 2.0},
			},
			ContinuousSeries{
				YAxis:   YAxisSecondary,
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{10.0, 11.0, 12.0, 13.0, 14.0},
			},
		},
	}

	xrange, yrange, yrangeAlt := c.getRanges()
	assert.Equal(-2.0, xrange.Min)
	assert.Equal(5.0, xrange.Max)

	assert.Equal(-2.1, yrange.Min)
	assert.Equal(4.5, yrange.Max)

	assert.Equal(10.0, yrangeAlt.Min)
	assert.Equal(14.0, yrangeAlt.Max)

	cSet := Chart{
		XAxis: XAxis{
			Range: Range{Min: 9.8, Max: 19.8},
		},
		YAxis: YAxis{
			Range: Range{Min: 9.9, Max: 19.9},
		},
		YAxisSecondary: YAxis{
			Range: Range{Min: 9.7, Max: 19.7},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
			ContinuousSeries{
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{-2.1, -1.0, 0, 1.0, 2.0},
			},
			ContinuousSeries{
				YAxis:   YAxisSecondary,
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{10.0, 11.0, 12.0, 13.0, 14.0},
			},
		},
	}

	xr2, yr2, yra2 := cSet.getRanges()
	assert.Equal(9.8, xr2.Min)
	assert.Equal(19.8, xr2.Max)

	assert.Equal(9.9, yr2.Min)
	assert.Equal(19.9, yr2.Max)

	assert.Equal(9.7, yra2.Min)
	assert.Equal(19.7, yra2.Max)
}

func TestChartGetRangesUseTicks(t *testing.T) {
	assert := assert.New(t)

	// this test asserts that ticks should supercede manual ranges when generating the overall ranges.

	c := Chart{
		YAxis: YAxis{
			Ticks: []Tick{
				{0.0, "Zero"},
				{1.0, "1.0"},
				{2.0, "2.0"},
				{3.0, "3.0"},
				{4.0, "4.0"},
				{5.0, "Five"},
			},
			Range: Range{
				Min: -5.0,
				Max: 5.0,
			},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
		},
	}

	xr, yr, yar := c.getRanges()
	assert.Equal(-2.0, xr.Min)
	assert.Equal(2.0, xr.Max)
	assert.Equal(0.0, yr.Min)
	assert.Equal(5.0, yr.Max)
	assert.True(yar.IsZero(), yar.String())
}

func TestChartGetRangesUseUserRanges(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		YAxis: YAxis{
			Range: Range{
				Min: -5.0,
				Max: 5.0,
			},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
		},
	}

	xr, yr, yar := c.getRanges()
	assert.Equal(-2.0, xr.Min)
	assert.Equal(2.0, xr.Max)
	assert.Equal(-5.0, yr.Min)
	assert.Equal(5.0, yr.Max)
	assert.True(yar.IsZero(), yar.String())
}

func TestChartGetBackgroundStyle(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Background: Style{
			FillColor: drawing.ColorBlack,
		},
	}

	bs := c.getBackgroundStyle()
	assert.Equal(bs.FillColor.String(), drawing.ColorBlack.String())
}

func TestChartGetCanvasStyle(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Canvas: Style{
			FillColor: drawing.ColorBlack,
		},
	}

	bs := c.getCanvasStyle()
	assert.Equal(bs.FillColor.String(), drawing.ColorBlack.String())
}

func TestChartGetDefaultCanvasBox(t *testing.T) {
	assert := assert.New(t)

	c := Chart{}
	canvasBoxDefault := c.getDefaultCanvasBox()
	assert.False(canvasBoxDefault.IsZero())
	assert.Equal(DefaultBackgroundPadding.Top, canvasBoxDefault.Top)
	assert.Equal(DefaultBackgroundPadding.Left, canvasBoxDefault.Left)
	assert.Equal(c.GetWidth()-DefaultBackgroundPadding.Right, canvasBoxDefault.Right)
	assert.Equal(c.GetHeight()-DefaultBackgroundPadding.Bottom, canvasBoxDefault.Bottom)

	custom := Chart{
		Background: Style{
			Padding: Box{
				Top:    DefaultBackgroundPadding.Top + 1,
				Left:   DefaultBackgroundPadding.Left + 1,
				Right:  DefaultBackgroundPadding.Right + 1,
				Bottom: DefaultBackgroundPadding.Bottom + 1,
			},
		},
	}
	canvasBoxCustom := custom.getDefaultCanvasBox()
	assert.False(canvasBoxCustom.IsZero())
	assert.Equal(DefaultBackgroundPadding.Top+1, canvasBoxCustom.Top)
	assert.Equal(DefaultBackgroundPadding.Left+1, canvasBoxCustom.Left)
	assert.Equal(c.GetWidth()-(DefaultBackgroundPadding.Right+1), canvasBoxCustom.Right)
	assert.Equal(c.GetHeight()-(DefaultBackgroundPadding.Bottom+1), canvasBoxCustom.Bottom)
}

func TestChartGetValueFormatters(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{-2.0, -1.0, 0, 1.0, 2.0},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
			ContinuousSeries{
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{-2.1, -1.0, 0, 1.0, 2.0},
			},
			ContinuousSeries{
				YAxis:   YAxisSecondary,
				XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
				YValues: []float64{10.0, 11.0, 12.0, 13.0, 14.0},
			},
		},
	}

	dxf, dyf, dyaf := c.getValueFormatters()
	assert.NotNil(dxf)
	assert.NotNil(dyf)
	assert.NotNil(dyaf)
}

func TestChartHasAxes(t *testing.T) {
	assert := assert.New(t)

	assert.False(Chart{}.hasAxes())

	x := Chart{
		XAxis: XAxis{
			Style: Style{
				Show: true,
			},
		},
	}
	assert.True(x.hasAxes())

	y := Chart{
		YAxis: YAxis{
			Style: Style{
				Show: true,
			},
		},
	}
	assert.True(y.hasAxes())

	ya := Chart{
		YAxisSecondary: YAxis{
			Style: Style{
				Show: true,
			},
		},
	}
	assert.True(ya.hasAxes())
}

func TestChartGetAxesTicks(t *testing.T) {
	assert := assert.New(t)

	r, err := PNG(1024, 1024)
	assert.Nil(err)

	c := Chart{
		XAxis: XAxis{
			Style: Style{Show: true},
			Range: Range{Min: 9.8, Max: 19.8},
		},
		YAxis: YAxis{
			Style: Style{Show: true},
			Range: Range{Min: 9.9, Max: 19.9},
		},
		YAxisSecondary: YAxis{
			Style: Style{Show: true},
			Range: Range{Min: 9.7, Max: 19.7},
		},
	}
	xr, yr, yar := c.getRanges()

	xt, yt, yat := c.getAxesTicks(r, xr, yr, yar, FloatValueFormatter, FloatValueFormatter, FloatValueFormatter)
	assert.NotEmpty(xt)
	assert.NotEmpty(yt)
	assert.NotEmpty(yat)
}

func TestChartSingleSeries(t *testing.T) {
	assert := assert.New(t)
	now := time.Now()
	c := Chart{
		Title:  "Hello!",
		Width:  1024,
		Height: 400,
		YAxis: YAxis{
			Range: Range{
				Min: 0.0,
				Max: 4.0,
			},
		},
		Series: []Series{
			TimeSeries{
				Name:    "goog",
				XValues: []time.Time{now.AddDate(0, 0, -3), now.AddDate(0, 0, -2), now.AddDate(0, 0, -1)},
				YValues: []float64{1.0, 2.0, 3.0},
			},
		},
	}

	buffer := bytes.NewBuffer([]byte{})
	c.Render(PNG, buffer)
	assert.NotEmpty(buffer.Bytes())
}

func TestChartRegressionBadRanges(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		Series: []Series{
			ContinuousSeries{
				XValues: []float64{math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1)},
				YValues: []float64{1.0, 2.0, 3.0, 4.0, 4.5},
			},
		},
	}
	buffer := bytes.NewBuffer([]byte{})
	c.Render(PNG, buffer)
	assert.True(true, "Render needs to finish.")
}

func TestChartRegressionBadRangesByUser(t *testing.T) {
	assert := assert.New(t)

	c := Chart{
		YAxis: YAxis{
			Range: Range{
				Min: math.Inf(-1),
				Max: math.Inf(1), // this could really happen? eh.
			},
		},
		Series: []Series{
			ContinuousSeries{
				XValues: Seq(1.0, 10.0),
				YValues: Seq(1.0, 10.0),
			},
		},
	}
	buffer := bytes.NewBuffer([]byte{})
	c.Render(PNG, buffer)
	assert.True(true, "Render needs to finish.")
}
<|endoftext|>