<commit_before>// Package implementing methods: flickr.photosets.*
package photosets

import (
	"github.com/masci/flickr.go/flickr"
)

type PhotosetsListResponse struct {
	flickr.BasicResponse
	Photosets struct {
		Page    int `xml:"page,attr"`
		Pages   int `xml:"pages,attr"`
		Perpage int `xml:"perpage,attr"`
		Total   int `xml:"total,attr"`
		Items   []struct {
			Id                string `xml:"id,attr"`
			Primary           string `xml:"primary,attr"`
			Secret            string `xml:"secret,attr"`
			Server            string `xml:"server,attr"`
			Farm              string `xml:"farm,attr"`
			Photos            int    `xml:"photos,attr"`
			Videos            int    `xml:"videos,attr"`
			NeedsInterstitial bool   `xml:"needs_interstitial,attr"`
			VisCanSeeSet      bool   `xml:"visibility_can_see_set,attr"`
			CountViews        int    `xml:"count_views,attr"`
			CountComments     int    `xml:"count_comments,attr"`
			CanComment        bool   `xml:"can_comment,attr"`
			DateCreate        int    `xml:"date_create,attr"`
			DateUpdate        int    `xml:"date_update,attr"`
			Title             string `xml:"title"`
			Description       string `xml:"description"`
		} `xml:"photoset"`
	} `xml:"photosets"`
}

// Return all the photosets belonging to the caller user
// This call must be signed to get both public and private sets
func GetOwnList(client *flickr.FlickrClient) (*PhotosetsListResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.ClearArgs()
	client.Args.Set("method", "flickr.photosets.getList")
	client.Args.Set("api_key", client.ApiKey)

	client.ApiSign(client.ApiSecret)

	response := &PhotosetsListResponse{}
	err := flickr.DoGet(client, response)
	return response, err
}

// Return the public sets belonging to the user with userId
// This method does not require authentication.
func GetList(client *flickr.FlickrClient, userId string) (*PhotosetsListResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.ClearArgs()
	client.Args.Set("method", "flickr.photosets.getList")
	client.Args.Set("api_key", client.ApiKey)
	client.Args.Set("user_id", userId)

	response := &PhotosetsListResponse{}
	err := flickr.DoGet(client, response)
	return response, err
}

// TODO docs
// This method requires authentication with 'write' permission.
func AddPhoto(client *flickr.FlickrClient, photosetId, photoId int) (*flickr.BasicResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.ClearArgs()
	client.Args.Set("method", "flickr.photosets.getList")
	client.Args.Set("api_key", client.ApiKey)

	client.ApiSign(client.ApiSecret)

	response := &flickr.BasicResponse{}
	err := flickr.DoPost(client, response)
	return response, err
}
<commit_msg>flickr.photsets.create<commit_after>// Package implementing methods: flickr.photosets.*
package photosets

import (
	"fmt"

	"github.com/masci/flickr.go/flickr"
)

type Photoset struct {
	Id                string `xml:"id,attr"`
	Primary           string `xml:"primary,attr"`
	Secret            string `xml:"secret,attr"`
	Server            string `xml:"server,attr"`
	Farm              string `xml:"farm,attr"`
	Photos            int    `xml:"photos,attr"`
	Videos            int    `xml:"videos,attr"`
	NeedsInterstitial bool   `xml:"needs_interstitial,attr"`
	VisCanSeeSet      bool   `xml:"visibility_can_see_set,attr"`
	CountViews        int    `xml:"count_views,attr"`
	CountComments     int    `xml:"count_comments,attr"`
	CanComment        bool   `xml:"can_comment,attr"`
	DateCreate        int    `xml:"date_create,attr"`
	DateUpdate        int    `xml:"date_update,attr"`
	Title             string `xml:"title"`
	Description       string `xml:"description"`
	Url               string `xml:"url,attr"`
}

type PhotosetsListResponse struct {
	flickr.BasicResponse
	Photosets struct {
		Page    int        `xml:"page,attr"`
		Pages   int        `xml:"pages,attr"`
		Perpage int        `xml:"perpage,attr"`
		Total   int        `xml:"total,attr"`
		Items   []Photoset `xml:"photoset"`
	} `xml:"photosets"`
}

type PhotosetResponse struct {
	flickr.BasicResponse
	Set Photoset `xml:"photoset"`
}

// Return all the photosets belonging to the caller user
// This call must be signed to get both public and private sets
func GetOwnList(client *flickr.FlickrClient) (*PhotosetsListResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.ClearArgs()
	client.Args.Set("method", "flickr.photosets.getList")
	client.Args.Set("api_key", client.ApiKey)

	client.ApiSign(client.ApiSecret)

	response := &PhotosetsListResponse{}
	err := flickr.DoGet(client, response)
	return response, err
}

// Return the public sets belonging to the user with userId
// This method does not require authentication.
func GetList(client *flickr.FlickrClient, userId string) (*PhotosetsListResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.ClearArgs()
	client.Args.Set("method", "flickr.photosets.getList")
	client.Args.Set("api_key", client.ApiKey)
	client.Args.Set("user_id", userId)

	response := &PhotosetsListResponse{}
	err := flickr.DoGet(client, response)
	return response, err
}

// TODO docs
// This method requires authentication with 'write' permission.
func AddPhoto(client *flickr.FlickrClient, photosetId, photoId int) (*flickr.BasicResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.ClearArgs()
	client.Args.Set("method", "flickr.photosets.getList")
	client.Args.Set("api_key", client.ApiKey)

	client.ApiSign(client.ApiSecret)

	response := &flickr.BasicResponse{}
	err := flickr.DoPost(client, response)
	return response, err
}

// TODO docs
// This method requires authentication with 'write' permission.
func Create(client *flickr.FlickrClient, title, description string, primaryPhotoId int) (*PhotosetResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.HTTPVerb = "POST"
	client.SetDefaultArgs()
	client.Args.Set("method", "flickr.photosets.create")
	client.Args.Set("oauth_token", client.OAuthToken)
	client.Args.Set("oauth_consumer_key", client.ApiKey)
	client.Args.Set("api_key", client.ApiKey)
	client.Args.Set("title", title)
	client.Args.Set("description", description)
	client.Args.Set("primary_photo_id", fmt.Sprintf("%d", primaryPhotoId))

	client.Sign(client.OAuthTokenSecret)

	response := &PhotosetResponse{}
	err := flickr.DoPost(client, response)
	return response, err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"

	"diskette/collections"
	"diskette/middleware"
	"diskette/rest"
	"diskette/user"

	"github.com/labstack/echo"
	"labix.org/v2/mgo"
)

type config struct {
	Database string `json:"database"`
	JwtKey   string `json:"jwtKey"`
}

func main() {
	cfg := readConfig()
	jwtKey := []byte(cfg.JwtKey)

	mongoSession := createMongoSession()
	defer mongoSession.Close()

	db := mongoSession.DB(cfg.Database)
	userCollection := db.C(collections.UserCollectionName)

	e := echo.New()

	userService := user.NewService(userCollection, jwtKey)
	user := e.Group("/user")
	user.Post("/signup", userService.Signup)
	user.Post("/confirm", userService.ConfirmSignup)
	user.Post("/signin", userService.Signin)
	user.Post("/forgot-password", userService.ForgotPassword)
	user.Post("/reset-password", userService.ResetPassword)

	sessionMiddleware := middleware.CreateSessionMiddleware(userCollection, jwtKey)
	private := e.Group("/private", sessionMiddleware)
	private.Post("/signout", userService.Signout)
	private.Post("/change-password", userService.ChangePassword)
	// private.Post("/update-profile", userService.UpdateProfile)

	restService := rest.NewService(db)
	e.Get("/:collection", restService.Get)
	e.Post("/:collection", restService.Post)
	e.Put("/:collection", restService.Put)
	e.Delete("/:collection", restService.Delete)

	fmt.Println("Listening at http://localhost:5025")
	e.Run(":5025")
}

func readConfig() config {
	var cfg config
	cfgData, err := ioutil.ReadFile("config.json")
	if err != nil {
		log.Fatal(err)
	}
	if err := json.Unmarshal(cfgData, &cfg); err != nil {
		log.Fatal(err)
	}
	return cfg
}

func createMongoSession() *mgo.Session {
	session, err := mgo.Dial("127.0.0.1")
	if err != nil {
		log.Fatal(err)
	}
	return session
}
<commit_msg>update<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"

	"diskette/collections"
	"diskette/middleware"
	"diskette/rest"
	"diskette/user"

	"github.com/labstack/echo"
	"labix.org/v2/mgo"
)

type config struct {
	Database string `json:"database"`
	JwtKey   string `json:"jwtKey"`
}

func main() {
	cfg := readConfig()
	jwtKey := []byte(cfg.JwtKey)

	mongoSession := createMongoSession()
	defer mongoSession.Close()

	db := mongoSession.DB(cfg.Database)
	userCollection := db.C(collections.UserCollectionName)

	e := echo.New()

	userService := user.NewService(userCollection, jwtKey)
	user := e.Group("/user")
	user.Post("/signup", userService.Signup)
	user.Post("/confirm", userService.ConfirmSignup)
	user.Post("/signin", userService.Signin)
	user.Post("/forgot-password", userService.ForgotPassword)
	user.Post("/reset-password", userService.ResetPassword)

	sessionMiddleware := middleware.CreateSessionMiddleware(userCollection, jwtKey)
	private := e.Group("/private", sessionMiddleware)
	private.Post("/signout", userService.Signout)
	private.Post("/change-password", userService.ChangePassword)
	private.Post("/change-email", userService.ChangeEmail)
	// private.Post("/update-profile", userService.UpdateProfile)

	restService := rest.NewService(db)
	e.Get("/:collection", restService.Get)
	e.Post("/:collection", restService.Post)
	e.Put("/:collection", restService.Put)
	e.Delete("/:collection", restService.Delete)

	fmt.Println("Listening at http://localhost:5025")
	e.Run(":5025")
}

func readConfig() config {
	var cfg config
	cfgData, err := ioutil.ReadFile("config.json")
	if err != nil {
		log.Fatal(err)
	}
	if err := json.Unmarshal(cfgData, &cfg); err != nil {
		log.Fatal(err)
	}
	return cfg
}

func createMongoSession() *mgo.Session {
	session, err := mgo.Dial("127.0.0.1")
	if err != nil {
		log.Fatal(err)
	}
	return session
}
<|endoftext|>


<commit_before>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package dll

import (
	"golang.org/x/text/encoding/korean"
	"strings"
	"unsafe"
)

const 단위_오프셋 = unsafe.Sizeof(byte(0))

// ANSI형식 DLL호출 문자열 변환
func F2ANSI문자열(go문자열 string) uintptr {
	ASCII_문자열 := append([]byte(go문자열), 0)

	return uintptr(unsafe.Pointer(&ASCII_문자열[0]))
}

func F2Go문자열(c문자열_포인터 unsafe.Pointer) string {
	return string(F2Go바이트_모음(c문자열_포인터))
}

func F2문자열_EUC_KR(c문자열_포인터 unsafe.Pointer) string {
	return f2문자열_EUC_KR(F2Go바이트_모음(c문자열_포인터))
}

func f2문자열_EUC_KR(바이트_모음 []byte) string {
	null문자_인덱스 := strings.Index(string(바이트_모음), "\x00")

	if null문자_인덱스 >= 0 {
		바이트_모음 = 바이트_모음[:null문자_인덱스]
	}

	바이트_모음_utf8, 에러 := korean.EUCKR.NewDecoder().Bytes(바이트_모음)
	if 에러 != nil {
		if len(바이트_모음) > 0 {
			return f2문자열_EUC_KR(바이트_모음[:len(바이트_모음)-1])
		}

		return string(바이트_모음)
	}

	return string(바이트_모음_utf8)
}

func F2Go바이트_모음(c데이터 unsafe.Pointer) []byte {
	바이트_모음 := make([]byte, 0)

	for i:=0 ; i<2048 ; i++ {
		포인터 := (*byte)(unsafe.Pointer(uintptr(c데이터) + uintptr(i)*단위_오프셋))
		바이트 := *포인터

		if 바이트 == 0 {
			return 바이트_모음
		}

		바이트_모음 = append(바이트_모음, 바이트)
	}

	return 바이트_모음
}

func F2Go바이트_모음with길이(c데이터 unsafe.Pointer, 길이 int) []byte {
	바이트_모음 := make([]byte, 길이)

	for i:=0 ; i<길이 ; i++ {
		포인터 := (*byte)(unsafe.Pointer(uintptr(c데이터) + uintptr(i)*단위_오프셋))
		바이트_모음[i] = *포인터
	}

	return 바이트_모음
}
<commit_msg>lib.dll.F2Go바이트_모음() 한도 상향 조정<commit_after>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package dll

import (
	"golang.org/x/text/encoding/korean"
	"strings"
	"unsafe"
)

const 단위_오프셋 = unsafe.Sizeof(byte(0))

// ANSI형식 DLL호출 문자열 변환
func F2ANSI문자열(go문자열 string) uintptr {
	ASCII_문자열 := append([]byte(go문자열), 0)

	return uintptr(unsafe.Pointer(&ASCII_문자열[0]))
}

func F2Go문자열(c문자열_포인터 unsafe.Pointer) string {
	return string(F2Go바이트_모음(c문자열_포인터))
}

func F2문자열_EUC_KR(c문자열_포인터 unsafe.Pointer) string {
	return f2문자열_EUC_KR(F2Go바이트_모음(c문자열_포인터))
}

func f2문자열_EUC_KR(바이트_모음 []byte) string {
	null문자_인덱스 := strings.Index(string(바이트_모음), "\x00")

	if null문자_인덱스 >= 0 {
		바이트_모음 = 바이트_모음[:null문자_인덱스]
	}

	바이트_모음_utf8, 에러 := korean.EUCKR.NewDecoder().Bytes(바이트_모음)
	if 에러 != nil {
		if len(바이트_모음) > 0 {
			return f2문자열_EUC_KR(바이트_모음[:len(바이트_모음)-1])
		}

		return string(바이트_모음)
	}

	return string(바이트_모음_utf8)
}

func F2Go바이트_모음(c데이터 unsafe.Pointer) []byte {
	바이트_모음 := make([]byte, 0)

	for i:=0 ; i<4096 ; i++ {
		포인터 := (*byte)(unsafe.Pointer(uintptr(c데이터) + uintptr(i)*단위_오프셋))
		바이트 := *포인터

		if 바이트 == 0 {
			return 바이트_모음
		}

		바이트_모음 = append(바이트_모음, 바이트)
	}

	return 바이트_모음
}

func F2Go바이트_모음with길이(c데이터 unsafe.Pointer, 길이 int) []byte {
	바이트_모음 := make([]byte, 길이)

	for i:=0 ; i<길이 ; i++ {
		포인터 := (*byte)(unsafe.Pointer(uintptr(c데이터) + uintptr(i)*단위_오프셋))
		바이트_모음[i] = *포인터
	}

	return 바이트_모음
}
<|endoftext|>


<commit_before>// Copyright ©2015 The gonum 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 asm

import "testing"

func TestDaxpyUnitary(t *testing.T) {
	for i, test := range []struct {
		alpha float64
		xData []float64
		yData []float64

		want []float64
	}{
		{
			alpha: 0,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-3},
		},
		{
			alpha: 1,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-1},
		},
		{
			alpha: 3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{3},
		},
		{
			alpha: -3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-9},
		},
		{
			alpha: 0,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 0, 3, -4, 5, -6},
		},
		{
			alpha: 1,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 1, 4, -2, 2, -10},
		},
		{
			alpha: 3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 3, 6, 2, -4, -18},
		},
		{
			alpha: -3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, -3, 0, -10, 14, 6},
		},
	} {
		x, xFront, xBack := newGuardedVector(test.xData, 1)
		y, yFront, yBack := newGuardedVector(test.yData, 1)
		z, zFront, zBack := newGuardedVector(test.xData, 1)

		DaxpyUnitary(test.alpha, x, y, z)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf("test %v: out-of-bounds write to x argument\nfront guard: %v\nback guard: %v\n",
				i, xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf("test %v: out-of-bounds write to y argument\nfront guard: %v\nback guard: %v\n",
				i, yFront, yBack)
		}
		if !allNaN(zFront) || !allNaN(zBack) {
			t.Errorf("test %v: out-of-bounds write to z argument\nfront guard: %v\nback guard: %v\n",
				i, zFront, zBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("test %v: modified x argument", i)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("test %v: modified y argument", i)
		}

		if !equalStrided(test.want, z, 1) {
			t.Errorf("test %v: unexpected result:\nwant: %v\ngot: %v", i, test.want, z)
		}
	}
}
<commit_msg>Extend test for DaxpyUnitary<commit_after>// Copyright ©2015 The gonum 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 asm

import (
	"fmt"
	"testing"
)

func TestDaxpyUnitary(t *testing.T) {
	for i, test := range []struct {
		alpha float64
		xData []float64
		yData []float64

		want []float64
	}{
		{
			alpha: 0,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-3},
		},
		{
			alpha: 1,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-1},
		},
		{
			alpha: 3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{3},
		},
		{
			alpha: -3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-9},
		},
		{
			alpha: 0,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 0, 3, -4, 5, -6},
		},
		{
			alpha: 1,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 1, 4, -2, 2, -10},
		},
		{
			alpha: 3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 3, 6, 2, -4, -18},
		},
		{
			alpha: -3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, -3, 0, -10, 14, 6},
		},
		{
			alpha: -5,
			xData: []float64{0, 0, 1, 1, 2, -3, -4, 5},
			yData: []float64{0, 1, 0, 3, -4, 5, -6, 7},
			want:  []float64{0, 1, -5, -2, -14, 20, 14, -18},
		},
	} {
		const msgGuard = "%v: out-of-bounds write to %v argument\nfront guard: %v\nback guard: %v"

		// Test z = alpha * x + y.
		prefix := fmt.Sprintf("test %v (z=a*x+y)", i)
		x, xFront, xBack := newGuardedVector(test.xData, 1)
		y, yFront, yBack := newGuardedVector(test.yData, 1)
		z, zFront, zBack := newGuardedVector(test.xData, 1)
		DaxpyUnitary(test.alpha, x, y, z)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !allNaN(zFront) || !allNaN(zBack) {
			t.Errorf(msgGuard, prefix, "z", zFront, zBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("%v: modified read-only x argument", prefix)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("%v: modified read-only y argument", prefix)
		}

		if !equalStrided(test.want, z, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, z)
		}

		// Test y = alpha * x + y.
		prefix = fmt.Sprintf("test %v (y=a*x+y)", i)
		x, xFront, xBack = newGuardedVector(test.xData, 1)
		y, yFront, yBack = newGuardedVector(test.yData, 1)
		DaxpyUnitary(test.alpha, x, y, y)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("%v: modified read-only x argument", prefix)
		}

		if !equalStrided(test.want, y, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, y)
		}

		// Test x = alpha * x + y.
		prefix = fmt.Sprintf("test %v (x=a*x+y)", i)
		x, xFront, xBack = newGuardedVector(test.xData, 1)
		y, yFront, yBack = newGuardedVector(test.yData, 1)

		DaxpyUnitary(test.alpha, x, y, x)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("%v: modified read-only y argument", prefix)
		}

		if !equalStrided(test.want, x, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, x)
		}
	}
}
<|endoftext|>


<commit_before>package handlers

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

	"github.com/cloudfoundry-samples/test-app/helpers"
)

type Hello struct {
	Time time.Time
}

func (p *Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	index, _ := helpers.FetchIndex()

	styledTemplate.Execute(w, Body{Body: fmt.Sprintf(`

	InnovAtos 2017

http://innovatos.hopto.org

My Index Is

%d

Uptime: %s

    `, index, time.Since(p.Time))})
}
<commit_msg>Update hello.go<commit_after>package handlers

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

	"github.com/cloudfoundry-samples/test-app/helpers"
)

type Hello struct {
	Time time.Time
}

func (p *Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	index, _ := helpers.FetchIndex()

	styledTemplate.Execute(w, Body{Body: fmt.Sprintf(`

	InnovAtos 2016

innovatos.hopto.org

My Index Is

%d

Uptime: %s

    `, index, time.Since(p.Time))})
}
<|endoftext|>


<commit_before>package handlers

import "net/http"

func Index(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Rooooaaar!!\n"))

	appendLionHeaders(w)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
}
<commit_msg>Write headers before body<commit_after>package handlers

import "net/http"

func Index(w http.ResponseWriter, r *http.Request) {
	appendLionHeaders(w)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Rooooaaar!!\n"))
}
<|endoftext|>


<commit_before>package core

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/netice9/cine"
)

var (
	ErrApplicationAlreadyExists = errors.New("Application already exists")
	ErrApplicationNotFound      = errors.New("Application not found")
	ErrGoalNotFound             = errors.New("Goal not found")
)

type Application struct {
	cine.Actor
	Name                string
	Configuration       *ApplicationConfiguration
	Goals               map[string]*Goal
	MainGoal            string
	ApplicationFileName string
	DockerClient        *docker.Client
}

type ApplicationStatus struct {
	Name     string                 `json:"name"`
	Goals    map[string]*GoalStatus `json:"goals"`
	MainGoal string                 `json:"main_goal"`
}

func (app *Application) GoalStatusUpdate(goalName, status string) {
	cine.Cast(app.Self(), nil, (*Application).goalStatusUpdate, goalName, status)
}

func (app *Application) goalStatusUpdate(goalName, status string) {
	for name, goal := range app.Goals {
		if name != goalName {
			goal.SiblingStatusUpdate(goalName, status)
		}
	}
}

func (app *Application) Status() *ApplicationStatus {
	res, err := cine.Call(app.Self(), (*Application).status)

	if err != nil {
		panic(err)
	}

	status := (*ApplicationStatus)(nil)

	status, _ = res[0].(*ApplicationStatus)

	return status
}

func (app *Application) status() *ApplicationStatus {
	goals := map[string]*GoalStatus{}

	for name, goal := range app.Goals {
		goals[name] = goal.Status()
	}

	return &ApplicationStatus{
		Name:     app.Name,
		Goals:    goals,
		MainGoal: app.MainGoal,
	}
}

func (app *Application) Logs(goalName string, w io.Writer) error {
	return app.Goals[goalName].Logs(w)
}

func (app *Application) Inspect(goalName string) (*docker.Container, error) {
	if app == nil {
		return nil, ErrApplicationNotFound
	}
	if goal, ok := app.Goals[goalName]; ok {
		return goal.Inspect()
	} else {
		return nil, ErrGoalNotFound
	}
}

func (app *Application) TransitionLog(goalName string) ([]TransitionLogEntry, error) {
	if app == nil {
		return nil, ErrApplicationNotFound
	}

	res, err := cine.Call(app.Self(), (*Application).transitionLog, goalName)

	if err != nil {
		panic(err)
	}

	entries := ([]TransitionLogEntry)(nil)

	entries, _ = res[0].([]TransitionLogEntry)

	err2 := (error)(nil)

	err2, _ = res[1].(error)

	return entries, err2
}

func (app *Application) transitionLog(goalName string) ([]TransitionLogEntry, error) {
	if goal, ok := app.Goals[goalName]; ok {
		return goal.TransitionLog(), nil
	} else {
		return nil, ErrGoalNotFound
	}
}

func (app *Application) Stats(goalName string, since time.Time) (*Stats, error) {

	if app == nil {
		return nil, ErrApplicationNotFound
	}

	res, err := cine.Call(app.Self(), (*Application).stats, goalName, since)

	if err != nil {
		panic(err)
	}

	stats := (*Stats)(nil)

	stats, _ = res[0].(*Stats)

	err2 := (error)(nil)

	err2, _ = res[1].(error)

	return stats, err2
}

func (app *Application) stats(goalName string, since time.Time) (*Stats, error) {
	if goal, ok := app.Goals[goalName]; ok {
		return goal.Stats(since), nil
	} else {
		return nil, ErrGoalNotFound
	}
}

func (app *Application) CurrentStats(goalName string) (*docker.Stats, error) {

	if app == nil {
		return nil, ErrApplicationNotFound
	}

	res, err := cine.Call(app.Self(), (*Application).currentStats, goalName)

	if err != nil {
		panic(err)
	}

	stats := (*docker.Stats)(nil)

	stats, _ = res[0].(*docker.Stats)

	err2 := (error)(nil)

	err2, _ = res[1].(error)

	return stats, err2
}

func (app *Application) currentStats(goalName string) (*docker.Stats, error) {
	if goal, ok := app.Goals[goalName]; ok {
		return goal.CurrentStats(), nil
	} else {
		return nil, ErrGoalNotFound
	}
}

func (app *Application) startGoals() {

	for goalName := range app.Configuration.Goals {
		app.Goals[goalName] = NewGoal(app, goalName, app.Name, app.Configuration.Goals, app.DockerClient)
	}

	app.Goals[app.MainGoal].Start()
}

func NewApplication(applicationName string, applicationConfiguration *ApplicationConfiguration, dockerClient *docker.Client) *Application {

	fileName := "/applications/" + applicationName + ".json"

	json, err := json.Marshal(applicationConfiguration)

	if err != nil {
		panic(err)
	}

	ioutil.WriteFile(fileName, json, 0644)

	app := &Application{
		Actor:               cine.Actor{},
		Name:                applicationName,
		Configuration:       applicationConfiguration,
		Goals:               map[string]*Goal{},
		MainGoal:            applicationConfiguration.MainGoal,
		ApplicationFileName: fileName,
		DockerClient:        dockerClient,
	}

	cine.StartActor(app)

	cine.Cast(app.Self(), nil, (*Application).startGoals)

	return app

}

func (app *Application) Terminate(errReason error) {
	os.Remove(app.ApplicationFileName)
	for _, goal := range app.Goals {
		goal.TerminateGoal()
	}
}

func (app *Application) TerminateApplication() {
	cine.Stop(app.Self())
}

func (app *Application) RequestGoalStart(name string) {
	cine.Cast(app.Self(), nil, (*Application).requestGoalStart, name)
}

func (app *Application) requestGoalStart(name string) {
	if goal, ok := app.Goals[name]; ok {
		goal.Start()
	} else {
		log.Error("Application ", app.Name, " requested start of uknown goal ", name)
	}
}

func (app *Application) HandleDockerEvent(evt *docker.APIEvents) {
	cine.Cast(app.Self(), nil, (*Application).handleDockerEvent, evt)
}

func (app *Application) handleDockerEvent(evt *docker.APIEvents) {
	for _, goal := range app.Goals {
		goal.HandleDockerEvent(evt)
	}
}
<commit_msg>removed actor from Application<commit_after>package core

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
)

var (
	ErrApplicationAlreadyExists = errors.New("Application already exists")
	ErrApplicationNotFound      = errors.New("Application not found")
	ErrGoalNotFound             = errors.New("Goal not found")
)

type Application struct {
	sync.Mutex
	Name                string
	Configuration       *ApplicationConfiguration
	Goals               map[string]*Goal
	MainGoal            string
	ApplicationFileName string
	DockerClient        *docker.Client
}

type ApplicationStatus struct {
	Name     string                 `json:"name"`
	Goals    map[string]*GoalStatus `json:"goals"`
	MainGoal string                 `json:"main_goal"`
}

func (a *Application) GoalStatusUpdate(goalName, status string) {
	a.Lock()
	defer a.Unlock()
	for name, goal := range a.Goals {
		if name != goalName {
			goal.SiblingStatusUpdate(goalName, status)
		}
	}
}

func (a *Application) copyGoals() map[string]*Goal {
	a.Lock()
	defer a.Unlock()
	goals := map[string]*Goal{}

	for name, goal := range a.Goals {
		goals[name] = goal
	}

	return goals
}

func (a *Application) Status() *ApplicationStatus {
	goals := a.copyGoals()
	goalStats := map[string]*GoalStatus{}

	for name, goal := range goals {
		goalStats[name] = goal.Status()
	}

	return &ApplicationStatus{
		Name:     a.Name,
		Goals:    goalStats,
		MainGoal: a.MainGoal,
	}
}

func (a *Application) goalByName(goalName string) (*Goal, error) {
	a.Lock()
	defer a.Unlock()

	goal, found := a.Goals[goalName]
	if !found {
		return nil, ErrGoalNotFound
	}
	return goal, nil
}
func (a *Application) Logs(goalName string, w io.Writer) error {
	goal, err := a.goalByName(goalName)
	if err != nil {
		return err
	}

	return goal.Logs(w)
}

func (a *Application) Inspect(goalName string) (*docker.Container, error) {
	if a == nil {
		return nil, ErrApplicationNotFound
	}
	goal, err := a.goalByName(goalName)
	if err != nil {
		return nil, err
	}
	return goal.Inspect()
}

func (a *Application) TransitionLog(goalName string) ([]TransitionLogEntry, error) {
	goal, err := a.goalByName(goalName)
	if err != nil {
		return nil, err
	}
	return goal.TransitionLog(), nil
}

func (a *Application) Stats(goalName string, since time.Time) (*Stats, error) {
	goal, err := a.goalByName(goalName)
	if err != nil {
		return nil, err
	}
	return goal.Stats(since), nil
}

func (a *Application) CurrentStats(goalName string) (*docker.Stats, error) {
	goal, err := a.goalByName(goalName)
	if err != nil {
		return nil, err
	}
	return goal.CurrentStats(), nil
}

func (a *Application) startGoals() {
	a.Lock()
	for goalName := range a.Configuration.Goals {
		a.Goals[goalName] = NewGoal(a, goalName, a.Name, a.Configuration.Goals, a.DockerClient)
	}
	a.Unlock()
	a.Goals[a.MainGoal].Start()
}

func NewApplication(applicationName string, applicationConfiguration *ApplicationConfiguration, dockerClient *docker.Client) *Application {

	fileName := "/applications/" + applicationName + ".json"

	json, err := json.Marshal(applicationConfiguration)

	if err != nil {
		panic(err)
	}

	ioutil.WriteFile(fileName, json, 0644)

	app := &Application{
		Name:                applicationName,
		Configuration:       applicationConfiguration,
		Goals:               map[string]*Goal{},
		MainGoal:            applicationConfiguration.MainGoal,
		ApplicationFileName: fileName,
		DockerClient:        dockerClient,
	}

	app.startGoals()

	return app

}

func (a *Application) TerminateApplication() {
	os.Remove(a.ApplicationFileName)
	for _, goal := range a.Goals {
		goal.TerminateGoal()
	}
}

func (a *Application) RequestGoalStart(name string) {
	if goal, ok := a.Goals[name]; ok {
		goal.Start()
	}
	log.Warn("Application ", a.Name, " requested start of uknown goal ", name)

}

func (a *Application) HandleDockerEvent(evt *docker.APIEvents) {
	goals := a.copyGoals()

	for _, goal := range goals {
		goal.HandleDockerEvent(evt)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/gobuild/log"
	"github.com/howeyc/fsnotify"
)

var verbose = flag.Bool("v", false, "show verbose")

func init() {
	log.SetFlags(0)
	if runtime.GOOS == "windows" {
		log.SetPrefix("fswatch >>> ")
	} else {
		log.SetPrefix("\033[32mfswatch\033[0m >>> ")
	}
}

const (
	CYELLOW = "33"
	CGREEN  = "32"
	CPURPLE = "35"
)

func cprintf(ansiColor string, format string, args ...interface{}) {
	if runtime.GOOS != "windows" {
		format = "\033[" + ansiColor + "m" + format + "\033[0m"
	}
	log.Printf(format, args...)
}

/*
type pathWatch struct {
	Include   string `json:"include"`
	reInclude *regexp.Regexp
	Exclude   string `json:"exclude"`
	reExclude *regexp.Regexp
	Depth     int `json:"depth"`
}

type fsWatch struct {
	PathWatches []pathWatch
	Command     []string `json:"command"`
	Cmd         string   `json:"cmd"` // if empty will add prefix(bash -c) and replace Command
	Signal      string   `json:"signal"`
	KillAsGroup bool     `json:"killasgroup"`
}

func init() {
	fw := fsWatch{
		PathWatches: []pathWatch{
			pathWatch{Include: "./", Exclude: "\\.svn"},
		},
		Cmd: "ls -l",
	}
	data, err := yaml.Marshal(fw)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(data))
}
*/

type gowatch struct {
	Paths     []string `json:"paths"`
	Depth     int      `json:"depth"`
	Exclude   []string `json:"exclude"`
	reExclude []*regexp.Regexp
	Include   []string `json:"include"`
	reInclude []*regexp.Regexp
	bufdur    time.Duration `json:"-"`
	Command   interface{}   `json:"command"` // can be string or []string
	cmd       []string
	Env       map[string]string `json:"env"`

	AutoRestart     bool          `json:"autorestart"`
	RestartInterval time.Duration `json:"restart-interval"`
	KillSignal      string        `json:"kill-signal"`

	w       *fsnotify.Watcher
	modtime map[string]time.Time
	sig     chan string
	sigOS   chan os.Signal
}

// Check if file matches
func (this *gowatch) match(file string) bool {
	file = filepath.Base(file)
	for _, rule := range this.reExclude {
		if rule.MatchString(file) {
			return false
		}
	}
	for _, rule := range this.reInclude {
		if rule.MatchString(file) {
			return true
		}
	}
	return len(this.reInclude) == 0 // if empty include, then return true
}

// Add dir and children (recursively) to watcher
func (this *gowatch) watchDirAndChildren(path string, depth int) error {
	if err := this.w.Watch(path); err != nil {
		return err
	}
	baseNumSeps := strings.Count(path, string(os.PathSeparator))
	return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			base := info.Name()
			if base != "." && strings.HasPrefix(base, ".") { // ignore hidden dir
				return filepath.SkipDir
			}

			pathDepth := strings.Count(path, string(os.PathSeparator)) - baseNumSeps
			if pathDepth > depth {
				return filepath.SkipDir
			}
			if *verbose {
				fmt.Println(">>> watch dir: ", path)
			}
			if err := this.w.Watch(path); err != nil {
				return err
			}
		}
		return nil
	})
}

// Create a fsnotify fswatch
// Initial vars
func (this *gowatch) Watch() (err error) {
	if this.w, err = fsnotify.NewWatcher(); err != nil {
		return
	}
	for _, path := range this.Paths {
		// translate env-vars
		if err = this.watchDirAndChildren(os.ExpandEnv(path), this.Depth); err != nil {
			log.Fatal(err)
		}
	}
	this.modtime = make(map[string]time.Time)
	this.sig = make(chan string)
	for _, patten := range this.Exclude {
		this.reExclude = append(this.reExclude, regexp.MustCompile(patten))
	}
	for _, patten := range this.Include {
		this.reInclude = append(this.reInclude, regexp.MustCompile(patten))
	}

	this.sigOS = make(chan os.Signal, 1)
	signal.Notify(this.sigOS, syscall.SIGINT)

	go this.drainExec()
	this.drainEvent()
	return
}

// filter fsevent and send to this.sig
func (this *gowatch) drainEvent() {
	for {
		select {
		case err := <-this.w.Error:
			log.Warnf("watch error: %s", err)
		case <-this.sigOS:
			this.sig <- "EXIT"
		case eve := <-this.w.Event:
			log.Debug(eve)
			changed := this.IsfileChanged(eve.Name)
			if changed && this.match(eve.Name) {
				log.Info(eve)
				select {
				case this.sig <- "KILL":
				default:
				}
			}
		}
	}
}

// Use modified time to judge if file changed
func (this *gowatch) IsfileChanged(p string) bool {
	p = filepath.Clean(p)
	fi, err := os.Stat(p)
	if err != nil {
		return true // if file not exists, just return true
	}
	curr := fi.ModTime()
	defer func() { this.modtime[p] = curr }()
	modt, ok := this.modtime[p]
	return !ok || curr.After(modt.Add(time.Second))
}

func (this *gowatch) drainExec() {
	log.Println("command:", this.cmd)
	var msg string
	for {
		startTime := time.Now()
		cmd := this.cmd
		if len(cmd) == 0 {
			cmd = []string{"echo", "no command specified"}
		}
		cprintf(CGREEN, "exec start")
		c := StartCmd(cmd[0], cmd[1:]...)
		// Start to run command
		err := c.Start()
		if err != nil {
			cprintf("35", err.Error())
		}
		// Wait until killed or finished
		select {
		case msg = <-this.sig:
			cprintf(CYELLOW, "program terminated, signal(%s)", this.KillSignal)
			if err := KillCmd(c, this.KillSignal); err != nil {
				log.Errorf("group kill: %v", err)
			}
			if msg == "EXIT" {
				os.Exit(1)
			}
			goto SKIP_WAITING
		case err = <-Go(c.Wait):
			if err != nil {
				cprintf(CPURPLE, "program exited: %v", err)
			}
		}
		log.Infof("finish in %s", time.Since(startTime))

		// Whether to restart right now
		if this.AutoRestart {
			goto SKIP_WAITING
		}
		cprintf("33", "-- wait signal --")
		if msg = <-this.sig; msg == "EXIT" {
			os.Exit(1)
		}
	SKIP_WAITING:
		if this.RestartInterval > 0 {
			log.Infof("restart after %s", this.RestartInterval)
		}
		time.Sleep(this.RestartInterval)
	}
}

const JSONCONF = ".fswatch.json"

var (
	gw = &gowatch{
		Paths:           []string{"."},
		Depth:           2,
		Exclude:         []string{},
		Include:         []string{"\\.(go|py|php|java|cpp|h|rb)$"},
		AutoRestart:     false,
		RestartInterval: 0,
		KillSignal:      "KILL",
	}
	confExists = false
	extInclude string
)

// parse command flag
func flagParse() {
	gw.Env = map[string]string{"POWERD_BY": "github.com/codeskyblue/fswatch"}
	// load JSONCONF
	if fd, err := os.Open(JSONCONF); err == nil {
		if er := json.NewDecoder(fd).Decode(gw); er != nil {
			log.Fatalf("json decode error: %v", er)
		}
		for key, val := range gw.Env {
			os.Setenv(key, val)
		}
		confExists = true
	}
	flag.DurationVar(&gw.RestartInterval, "ri", gw.RestartInterval, "restart interval")
	flag.BoolVar(&gw.AutoRestart, "r", gw.AutoRestart, "auto restart")
	flag.StringVar(&gw.KillSignal, "k", gw.KillSignal, "kill signal")
	flag.StringVar(&extInclude, "ext", "", "extention eg: [cpp,c,h]")
	flag.Parse()
}

func main() {
	flag.Usage = func() {
		fmt.Printf(`Usage: fswatch [OPTIONS...] <arg...>
OPTIONS:
    -ext=""    Extenton seprated by , eg: cpp,c,h
    -v         Show verbose info

Example:
    fswatch -ext cpp,c,h make -f Makefile
`)
	}
	flagParse()
	if len(os.Args) == 1 && !confExists {
		fmt.Printf("Create %s file [y/n]: ", strconv.Quote(JSONCONF))
		var yn string = "y"
		fmt.Scan(&yn)
		gw.Command = "echo helloworld"
		if strings.ToUpper(strings.TrimSpace(yn)) == "Y" {
			data, _ := json.MarshalIndent(gw, "", "    ")
			ioutil.WriteFile(JSONCONF, data, 0644)
			fmt.Printf("use notepad++ or vim to edit %s\n", strconv.Quote(JSONCONF))
		}
		return
	}

	if flag.NArg() > 0 {
		gw.Command = []string(flag.Args())
	}
	if extInclude != "" {
		for _, ext := range strings.Split(extInclude, ",") {
			gw.Include = append(gw.Include, "\\."+ext+"$")
		}
	}

	switch gw.Command.(type) {
	default:
		log.Fatal("check you config file. \"command\" must be string or []string")
	case string:
		if runtime.GOOS == "windows" {
			gw.cmd = []string{"cmd", "/c", gw.Command.(string)}
		} else {
			gw.cmd = []string{"bash", "-c", gw.Command.(string)}
		}
	case []string:
		gw.cmd = gw.Command.([]string)
	}

	log.Fatal(gw.Watch())
}
<commit_msg>Fix parsing command from config when array of strings<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/gobuild/log"
	"github.com/howeyc/fsnotify"
)

var verbose = flag.Bool("v", false, "show verbose")

func init() {
	log.SetFlags(0)
	if runtime.GOOS == "windows" {
		log.SetPrefix("fswatch >>> ")
	} else {
		log.SetPrefix("\033[32mfswatch\033[0m >>> ")
	}
}

const (
	CYELLOW = "33"
	CGREEN  = "32"
	CPURPLE = "35"
)

func cprintf(ansiColor string, format string, args ...interface{}) {
	if runtime.GOOS != "windows" {
		format = "\033[" + ansiColor + "m" + format + "\033[0m"
	}
	log.Printf(format, args...)
}

/*
type pathWatch struct {
	Include   string `json:"include"`
	reInclude *regexp.Regexp
	Exclude   string `json:"exclude"`
	reExclude *regexp.Regexp
	Depth     int `json:"depth"`
}

type fsWatch struct {
	PathWatches []pathWatch
	Command     []string `json:"command"`
	Cmd         string   `json:"cmd"` // if empty will add prefix(bash -c) and replace Command
	Signal      string   `json:"signal"`
	KillAsGroup bool     `json:"killasgroup"`
}

func init() {
	fw := fsWatch{
		PathWatches: []pathWatch{
			pathWatch{Include: "./", Exclude: "\\.svn"},
		},
		Cmd: "ls -l",
	}
	data, err := yaml.Marshal(fw)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(data))
}
*/

type gowatch struct {
	Paths     []string `json:"paths"`
	Depth     int      `json:"depth"`
	Exclude   []string `json:"exclude"`
	reExclude []*regexp.Regexp
	Include   []string `json:"include"`
	reInclude []*regexp.Regexp
	bufdur    time.Duration `json:"-"`
	Command   interface{}   `json:"command"` // can be string or []string
	cmd       []string
	Env       map[string]string `json:"env"`

	AutoRestart     bool          `json:"autorestart"`
	RestartInterval time.Duration `json:"restart-interval"`
	KillSignal      string        `json:"kill-signal"`

	w       *fsnotify.Watcher
	modtime map[string]time.Time
	sig     chan string
	sigOS   chan os.Signal
}

// Check if file matches
func (this *gowatch) match(file string) bool {
	file = filepath.Base(file)
	for _, rule := range this.reExclude {
		if rule.MatchString(file) {
			return false
		}
	}
	for _, rule := range this.reInclude {
		if rule.MatchString(file) {
			return true
		}
	}
	return len(this.reInclude) == 0 // if empty include, then return true
}

// Add dir and children (recursively) to watcher
func (this *gowatch) watchDirAndChildren(path string, depth int) error {
	if err := this.w.Watch(path); err != nil {
		return err
	}
	baseNumSeps := strings.Count(path, string(os.PathSeparator))
	return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			base := info.Name()
			if base != "." && strings.HasPrefix(base, ".") { // ignore hidden dir
				return filepath.SkipDir
			}

			pathDepth := strings.Count(path, string(os.PathSeparator)) - baseNumSeps
			if pathDepth > depth {
				return filepath.SkipDir
			}
			if *verbose {
				fmt.Println(">>> watch dir: ", path)
			}
			if err := this.w.Watch(path); err != nil {
				return err
			}
		}
		return nil
	})
}

// Create a fsnotify fswatch
// Initial vars
func (this *gowatch) Watch() (err error) {
	if this.w, err = fsnotify.NewWatcher(); err != nil {
		return
	}
	for _, path := range this.Paths {
		// translate env-vars
		if err = this.watchDirAndChildren(os.ExpandEnv(path), this.Depth); err != nil {
			log.Fatal(err)
		}
	}
	this.modtime = make(map[string]time.Time)
	this.sig = make(chan string)
	for _, patten := range this.Exclude {
		this.reExclude = append(this.reExclude, regexp.MustCompile(patten))
	}
	for _, patten := range this.Include {
		this.reInclude = append(this.reInclude, regexp.MustCompile(patten))
	}

	this.sigOS = make(chan os.Signal, 1)
	signal.Notify(this.sigOS, syscall.SIGINT)

	go this.drainExec()
	this.drainEvent()
	return
}

// filter fsevent and send to this.sig
func (this *gowatch) drainEvent() {
	for {
		select {
		case err := <-this.w.Error:
			log.Warnf("watch error: %s", err)
		case <-this.sigOS:
			this.sig <- "EXIT"
		case eve := <-this.w.Event:
			log.Debug(eve)
			changed := this.IsfileChanged(eve.Name)
			if changed && this.match(eve.Name) {
				log.Info(eve)
				select {
				case this.sig <- "KILL":
				default:
				}
			}
		}
	}
}

// Use modified time to judge if file changed
func (this *gowatch) IsfileChanged(p string) bool {
	p = filepath.Clean(p)
	fi, err := os.Stat(p)
	if err != nil {
		return true // if file not exists, just return true
	}
	curr := fi.ModTime()
	defer func() { this.modtime[p] = curr }()
	modt, ok := this.modtime[p]
	return !ok || curr.After(modt.Add(time.Second))
}

func (this *gowatch) drainExec() {
	log.Println("command:", this.cmd)
	var msg string
	for {
		startTime := time.Now()
		cmd := this.cmd
		if len(cmd) == 0 {
			cmd = []string{"echo", "no command specified"}
		}
		cprintf(CGREEN, "exec start")
		c := StartCmd(cmd[0], cmd[1:]...)
		// Start to run command
		err := c.Start()
		if err != nil {
			cprintf("35", err.Error())
		}
		// Wait until killed or finished
		select {
		case msg = <-this.sig:
			cprintf(CYELLOW, "program terminated, signal(%s)", this.KillSignal)
			if err := KillCmd(c, this.KillSignal); err != nil {
				log.Errorf("group kill: %v", err)
			}
			if msg == "EXIT" {
				os.Exit(1)
			}
			goto SKIP_WAITING
		case err = <-Go(c.Wait):
			if err != nil {
				cprintf(CPURPLE, "program exited: %v", err)
			}
		}
		log.Infof("finish in %s", time.Since(startTime))

		// Whether to restart right now
		if this.AutoRestart {
			goto SKIP_WAITING
		}
		cprintf("33", "-- wait signal --")
		if msg = <-this.sig; msg == "EXIT" {
			os.Exit(1)
		}
	SKIP_WAITING:
		if this.RestartInterval > 0 {
			log.Infof("restart after %s", this.RestartInterval)
		}
		time.Sleep(this.RestartInterval)
	}
}

const JSONCONF = ".fswatch.json"

var (
	gw = &gowatch{
		Paths:           []string{"."},
		Depth:           2,
		Exclude:         []string{},
		Include:         []string{"\\.(go|py|php|java|cpp|h|rb)$"},
		AutoRestart:     false,
		RestartInterval: 0,
		KillSignal:      "KILL",
	}
	confExists = false
	extInclude string
)

// parse command flag
func flagParse() {
	gw.Env = map[string]string{"POWERD_BY": "github.com/codeskyblue/fswatch"}
	// load JSONCONF
	if fd, err := os.Open(JSONCONF); err == nil {
		if er := json.NewDecoder(fd).Decode(gw); er != nil {
			log.Fatalf("json decode error: %v", er)
		}
		for key, val := range gw.Env {
			os.Setenv(key, val)
		}
		confExists = true
	}
	flag.DurationVar(&gw.RestartInterval, "ri", gw.RestartInterval, "restart interval")
	flag.BoolVar(&gw.AutoRestart, "r", gw.AutoRestart, "auto restart")
	flag.StringVar(&gw.KillSignal, "k", gw.KillSignal, "kill signal")
	flag.StringVar(&extInclude, "ext", "", "extention eg: [cpp,c,h]")
	flag.Parse()
}

func main() {
	flag.Usage = func() {
		fmt.Printf(`Usage: fswatch [OPTIONS...] <arg...>
OPTIONS:
    -ext=""    Extenton seprated by , eg: cpp,c,h
    -v         Show verbose info

Example:
    fswatch -ext cpp,c,h make -f Makefile
`)
	}
	flagParse()
	if len(os.Args) == 1 && !confExists {
		fmt.Printf("Create %s file [y/n]: ", strconv.Quote(JSONCONF))
		var yn string = "y"
		fmt.Scan(&yn)
		gw.Command = "echo helloworld"
		if strings.ToUpper(strings.TrimSpace(yn)) == "Y" {
			data, _ := json.MarshalIndent(gw, "", "    ")
			ioutil.WriteFile(JSONCONF, data, 0644)
			fmt.Printf("use notepad++ or vim to edit %s\n", strconv.Quote(JSONCONF))
		}
		return
	}

	if flag.NArg() > 0 {
		gw.Command = []string(flag.Args())
	}
	if extInclude != "" {
		for _, ext := range strings.Split(extInclude, ",") {
			gw.Include = append(gw.Include, "\\."+ext+"$")
		}
	}

	// []string is unmarshaled as []interface{}
	if v, ok := gw.Command.([]interface{}); ok {
		cmd := make([]string, 0, len(v))
		for _, i := range v {
			if s, ok := i.(string); ok {
				cmd = append(cmd, s)
			} else {
				log.Fatalf("check you config file. \"command\" must be string or []string, got %T", gw.Command)
			}
		}
		gw.Command = cmd
	}

	switch gw.Command.(type) {
	default:
		log.Fatalf("check you config file. \"command\" must be string or []string, got %T", gw.Command)
	case string:
		if runtime.GOOS == "windows" {
			gw.cmd = []string{"cmd", "/c", gw.Command.(string)}
		} else {
			gw.cmd = []string{"bash", "-c", gw.Command.(string)}
		}
	case []string:
		gw.cmd = gw.Command.([]string)
	}

	log.Fatal(gw.Watch())
}
<|endoftext|>


<commit_before>package bitswap

import (
	"errors"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"

	blocks "github.com/jbenet/go-ipfs/blocks"
	blockstore "github.com/jbenet/go-ipfs/blockstore"
	exchange "github.com/jbenet/go-ipfs/exchange"
	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
	bsnet "github.com/jbenet/go-ipfs/exchange/bitswap/network"
	notifications "github.com/jbenet/go-ipfs/exchange/bitswap/notifications"
	strategy "github.com/jbenet/go-ipfs/exchange/bitswap/strategy"
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

// TODO rename -> Router?
type Routing interface {
	// FindProvidersAsync returns a channel of providers for the given key
	// TODO replace with timeout with context
	FindProvidersAsync(context.Context, u.Key, int) <-chan *peer.Peer

	// Provide provides the key to the network
	Provide(key u.Key) error
}

// NewSession initializes a bitswap session.
func NewSession(parent context.Context, s bsnet.NetworkService, p *peer.Peer, d ds.Datastore, directory Routing) exchange.Interface {

	adapter := bsnet.NewNetworkAdapter(s, nil)
	bs := &bitswap{
		blockstore:    blockstore.NewBlockstore(d),
		notifications: notifications.New(),
		strategy:      strategy.New(),
		routing:       directory,
		sender:        adapter,
	}
	adapter.SetDelegate(bs)

	return bs
}

// bitswap instances implement the bitswap protocol.
type bitswap struct {

	// sender delivers messages on behalf of the session
	sender bsnet.NetworkAdapter

	// blockstore is the local database
	// NB: ensure threadsafety
	blockstore blockstore.Blockstore

	// routing interface for communication
	routing Routing

	notifications notifications.PubSub

	// strategy listens to network traffic and makes decisions about how to
	// interact with partners.
	// TODO(brian): save the strategy's state to the datastore
	strategy strategy.Strategy
}

// GetBlock attempts to retrieve a particular block from peers within the
// deadline enforced by the context
func (bs *bitswap) Block(ctx context.Context, k u.Key) (
	*blocks.Block, error) {

	provs_ch := bs.routing.FindProvidersAsync(ctx, k, 20)

	blockChannel := make(chan blocks.Block)

	// TODO: when the data is received, shut down this for loop ASAP
	go func() {
		for p := range provs_ch {
			go func(pr *peer.Peer) {
				blk, err := bs.getBlock(ctx, k, pr)
				if err != nil {
					return
				}
				select {
				case blockChannel <- *blk:
				default:
				}
			}(p)
		}
	}()

	select {
	case block := <-blockChannel:
		close(blockChannel)
		return &block, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (bs *bitswap) getBlock(ctx context.Context, k u.Key, p *peer.Peer) (*blocks.Block, error) {

	blockChannel := bs.notifications.Subscribe(ctx, k)

	message := bsmsg.New()
	message.AppendWanted(k)

	bs.send(ctx, p, message)

	block, ok := <-blockChannel
	if !ok {
		return nil, u.ErrTimeout
	}
	return &block, nil
}

func (bs *bitswap) sendToPeersThatWant(ctx context.Context, block blocks.Block) {
	for _, p := range bs.strategy.Peers() {
		if bs.strategy.BlockIsWantedByPeer(block.Key(), p) {
			if bs.strategy.ShouldSendBlockToPeer(block.Key(), p) {
				message := bsmsg.New()
				message.AppendBlock(block)
				go bs.send(ctx, p, message)
			}
		}
	}
}

// HasBlock announces the existance of a block to bitswap, potentially sending
// it to peers (Partners) whose WantLists include it.
func (bs *bitswap) HasBlock(blk blocks.Block) error {
	ctx := context.TODO()
	go bs.sendToPeersThatWant(ctx, blk)
	return bs.routing.Provide(blk.Key())
}

// TODO(brian): handle errors
func (bs *bitswap) ReceiveMessage(
	ctx context.Context, p *peer.Peer, incoming bsmsg.BitSwapMessage) (
	*peer.Peer, bsmsg.BitSwapMessage, error) {

	bs.strategy.MessageReceived(p, incoming)

	if incoming.Blocks() != nil {
		for _, block := range incoming.Blocks() {
			go bs.blockstore.Put(block) // FIXME(brian): err ignored
			go bs.notifications.Publish(block)
		}
	}

	if incoming.Wantlist() != nil {
		for _, key := range incoming.Wantlist() {
			if bs.strategy.ShouldSendBlockToPeer(key, p) {
				block, errBlockNotFound := bs.blockstore.Get(key)
				if errBlockNotFound != nil {
					// TODO(brian): log/return the error
					continue
				}
				message := bsmsg.New()
				message.AppendBlock(*block)
				go bs.send(ctx, p, message)
			}
		}
	}
	return nil, nil, errors.New("TODO implement")
}

// send strives to ensure that accounting is always performed when a message is
// sent
func (bs *bitswap) send(ctx context.Context, p *peer.Peer, m bsmsg.BitSwapMessage) {
	bs.sender.SendMessage(context.Background(), p, m)
	bs.strategy.MessageSent(p, m)
}

func numBytes(b blocks.Block) int {
	return len(b.Data)
}
<commit_msg>fix(bitswap) use passed ctx<commit_after>package bitswap

import (
	"errors"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"

	blocks "github.com/jbenet/go-ipfs/blocks"
	blockstore "github.com/jbenet/go-ipfs/blockstore"
	exchange "github.com/jbenet/go-ipfs/exchange"
	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
	bsnet "github.com/jbenet/go-ipfs/exchange/bitswap/network"
	notifications "github.com/jbenet/go-ipfs/exchange/bitswap/notifications"
	strategy "github.com/jbenet/go-ipfs/exchange/bitswap/strategy"
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

// TODO rename -> Router?
type Routing interface {
	// FindProvidersAsync returns a channel of providers for the given key
	// TODO replace with timeout with context
	FindProvidersAsync(context.Context, u.Key, int) <-chan *peer.Peer

	// Provide provides the key to the network
	Provide(key u.Key) error
}

// NewSession initializes a bitswap session.
func NewSession(parent context.Context, s bsnet.NetworkService, p *peer.Peer, d ds.Datastore, directory Routing) exchange.Interface {

	adapter := bsnet.NewNetworkAdapter(s, nil)
	bs := &bitswap{
		blockstore:    blockstore.NewBlockstore(d),
		notifications: notifications.New(),
		strategy:      strategy.New(),
		routing:       directory,
		sender:        adapter,
	}
	adapter.SetDelegate(bs)

	return bs
}

// bitswap instances implement the bitswap protocol.
type bitswap struct {

	// sender delivers messages on behalf of the session
	sender bsnet.NetworkAdapter

	// blockstore is the local database
	// NB: ensure threadsafety
	blockstore blockstore.Blockstore

	// routing interface for communication
	routing Routing

	notifications notifications.PubSub

	// strategy listens to network traffic and makes decisions about how to
	// interact with partners.
	// TODO(brian): save the strategy's state to the datastore
	strategy strategy.Strategy
}

// GetBlock attempts to retrieve a particular block from peers within the
// deadline enforced by the context
func (bs *bitswap) Block(ctx context.Context, k u.Key) (
	*blocks.Block, error) {

	provs_ch := bs.routing.FindProvidersAsync(ctx, k, 20)

	blockChannel := make(chan blocks.Block)

	// TODO: when the data is received, shut down this for loop ASAP
	go func() {
		for p := range provs_ch {
			go func(pr *peer.Peer) {
				blk, err := bs.getBlock(ctx, k, pr)
				if err != nil {
					return
				}
				select {
				case blockChannel <- *blk:
				default:
				}
			}(p)
		}
	}()

	select {
	case block := <-blockChannel:
		close(blockChannel)
		return &block, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (bs *bitswap) getBlock(ctx context.Context, k u.Key, p *peer.Peer) (*blocks.Block, error) {

	blockChannel := bs.notifications.Subscribe(ctx, k)

	message := bsmsg.New()
	message.AppendWanted(k)

	bs.send(ctx, p, message)

	block, ok := <-blockChannel
	if !ok {
		return nil, u.ErrTimeout
	}
	return &block, nil
}

func (bs *bitswap) sendToPeersThatWant(ctx context.Context, block blocks.Block) {
	for _, p := range bs.strategy.Peers() {
		if bs.strategy.BlockIsWantedByPeer(block.Key(), p) {
			if bs.strategy.ShouldSendBlockToPeer(block.Key(), p) {
				message := bsmsg.New()
				message.AppendBlock(block)
				go bs.send(ctx, p, message)
			}
		}
	}
}

// HasBlock announces the existance of a block to bitswap, potentially sending
// it to peers (Partners) whose WantLists include it.
func (bs *bitswap) HasBlock(blk blocks.Block) error {
	ctx := context.TODO()
	go bs.sendToPeersThatWant(ctx, blk)
	return bs.routing.Provide(blk.Key())
}

// TODO(brian): handle errors
func (bs *bitswap) ReceiveMessage(
	ctx context.Context, p *peer.Peer, incoming bsmsg.BitSwapMessage) (
	*peer.Peer, bsmsg.BitSwapMessage, error) {

	bs.strategy.MessageReceived(p, incoming)

	if incoming.Blocks() != nil {
		for _, block := range incoming.Blocks() {
			go bs.blockstore.Put(block) // FIXME(brian): err ignored
			go bs.notifications.Publish(block)
		}
	}

	if incoming.Wantlist() != nil {
		for _, key := range incoming.Wantlist() {
			if bs.strategy.ShouldSendBlockToPeer(key, p) {
				block, errBlockNotFound := bs.blockstore.Get(key)
				if errBlockNotFound != nil {
					// TODO(brian): log/return the error
					continue
				}
				message := bsmsg.New()
				message.AppendBlock(*block)
				go bs.send(ctx, p, message)
			}
		}
	}
	return nil, nil, errors.New("TODO implement")
}

// send strives to ensure that accounting is always performed when a message is
// sent
func (bs *bitswap) send(ctx context.Context, p *peer.Peer, m bsmsg.BitSwapMessage) {
	bs.sender.SendMessage(ctx, p, m)
	bs.strategy.MessageSent(p, m)
}

func numBytes(b blocks.Block) int {
	return len(b.Data)
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"testing"
	"time"

	"github.com/miekg/dns"
)

func TestDNSResponse(t *testing.T) {
	const TEST_ADDR = "127.0.0.1:9953"

	config := NewConfig()
	config.dnsAddr = TEST_ADDR

	server := NewDNSServer(config)
	go server.Start()

	// Allow some time for server to start
	time.Sleep(250 * time.Millisecond)

	m := new(dns.Msg)
	m.Id = dns.Id()
	m.RecursionDesired = true
	m.Question = []dns.Question{
		dns.Question{"google.com.", dns.TypeA, dns.ClassINET},
	}
	c := new(dns.Client)
	in, _, err := c.Exchange(m, TEST_ADDR)

	if err != nil {
		t.Error("Error response from the server", err)
	}

	if len(in.Answer) < 3 {
		t.Error("DNS request only responded ", len(in.Answer), "answers")
	}

	server.AddService("foo", Service{Name: "foo", Image: "bar", Ip: net.ParseIP("127.0.0.1")})
	server.AddService("baz", Service{Name: "baz", Image: "bar", Ip: net.ParseIP("127.0.0.1"), Ttl: -1, Alias: "www.seznam.cz"})

	server.AddService("erw", Service{Name: "erw", Image: "bar", Ip: net.ParseIP("127.0.0.1"), Ttl: -1, Alias: "www.seznam.cz"})

	var inputs = []struct {
		query    string
		expected int
	}{
		{"docker.", 3},
		{"*.docker.", 3},
		{"bar.docker.", 3},
		{"foo.docker.", 0},
		{"baz.bar.docker.", 1},
		{"www.seznam.cz.", 2},
	}

	for _, input := range inputs {
		t.Log(input.query)
		m := new(dns.Msg)
		m.Id = dns.Id()
		m.RecursionDesired = true
		m.Question = []dns.Question{
			dns.Question{input.query, dns.TypeA, dns.ClassINET},
		}
		c := new(dns.Client)
		in, _, err := c.Exchange(m, TEST_ADDR)
		if err != nil {
			t.Error("Error response from the server", err)
			break
		}
		if len(in.Answer) == 0 {
			t.Error(input, "No SOA anwer")
		}
		if _, ok := in.Answer[0].(*dns.SOA); ok {
			if input.expected != 0 {
				t.Error(input, "Expected:", input.expected, " Got:", 0)
			}
		} else if len(in.Answer) != input.expected {
			t.Error(input, "Expected:", input.expected, " Got:", len(in.Answer))
		}
	}

	// // This test is slow and pointless
	// server.Stop()
	//
	// c = new(dns.Client)
	// _, _, err = c.Exchange(m, TEST_ADDR)
	//
	// if err == nil {
	// 	t.Error("Server still running but should be shut down.")
	// }
}

func TestServiceManagement(t *testing.T) {
	list := ServiceListProvider(NewDNSServer(NewConfig()))

	if len(list.GetAllServices()) != 0 {
		t.Error("Initial service count should be 0.")
	}

	A := Service{Name: "bar"}
	list.AddService("foo", A)

	if len(list.GetAllServices()) != 1 {
		t.Error("Service count should be 1.")
	}

	A.Name = "baz"

	s1, err := list.GetService("foo")
	if err != nil {
		t.Error("GetService error", err)
	}

	if s1.Name != "bar" {
		t.Error("Expected: bar got:", s1.Name)
	}

	_, err = list.GetService("boo")

	if err == nil {
		t.Error("Request to boo should have failed")
	}

	list.AddService("boo", Service{Name: "boo"})

	all := list.GetAllServices()

	delete(all, "foo")
	s2 := all["boo"]
	s2.Name = "zoo"

	if len(list.GetAllServices()) != 2 {
		t.Error("Local map change should not remove items")
	}

	if s1, _ = list.GetService("boo"); s1.Name != "boo" {
		t.Error("Local map change should not change items")
	}

	err = list.RemoveService("bar")
	if err == nil {
		t.Error("Removing bar should fail")
	}

	err = list.RemoveService("foo")
	if err != nil {
		t.Error("Removing foo failed", err)
	}

	if len(list.GetAllServices()) != 1 {
		t.Error("Item count after remove should be 1")
	}

	list.AddService("416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df", Service{Name: "mysql"})

	if s1, _ = list.GetService("416261"); s1.Name != "mysql" {
		t.Error("Container can't be found by prefix")
	}

	err = list.RemoveService("416261")
	if err != nil {
		t.Error("Removing 416261 failed", err)
	}

	if len(list.GetAllServices()) != 1 {
		t.Error("Item count after remove should be 1")
	}

}

func TestAliasManagement(t *testing.T) {
	servId := "fsdfsdfsd"
	alias := "www.seznam.cz"
	s := NewDNSServer(NewConfig())
	s.AddService(servId, Service{Name: "mysql", Alias: alias})
	id_map, exists := s.aliases[alias]
	if len(id_map) != 1 {
		t.Error("should be only one alias")
	}
	_, id_is_there := id_map[servId]
	if !id_is_there {
		t.Error("alias registered under wrong id")
	}
	if !exists {
		t.Error("alias was not created")
	}
}

func TestDNSRequestMatch(t *testing.T) {
	server := NewDNSServer(NewConfig())

	server.AddService("foo", Service{Name: "foo", Image: "bar"})
	server.AddService("baz", Service{Name: "baz", Image: "bar"})
	server.AddService("abc", Service{Name: "def", Image: "ghi"})

	inputs := []struct {
		query, domain string
		expected      int
	}{
		{"docker", "docker", 3},
		{"baz.docker", "docker.local", 0},
		{"docker.local", "docker.local", 3},
		{"foo.docker.local", "docker.local", 0},
		{"bar.docker.local", "docker.local", 2},
		{"foo.bar.docker.local", "docker.local", 1},
		{"*.local", "docker.local", 3},
		{"*.docker.local", "docker.local", 3},
		{"bar.*.local", "docker.local", 2},
		{"*.*.local", "docker.local", 3},
		{"foo.*.local", "docker.local", 0},
		{"bar.*.docker.local", "docker.local", 0},
		{"foo.*.docker", "docker", 1},
		{"baz.foo.bar.docker.local", "docker.local", 1},
		{"foo.bar", "baz.foo.bar", 3},
	}

	for _, input := range inputs {
		server.config.domain = NewDomain(input.domain)

		t.Log(input.query, input.domain)

		actual := 0
		for _ = range server.queryServices(input.query) {
			actual++
		}

		if actual != input.expected {
			t.Error(input, "Expected:", input.expected, "Got:", actual)
		}
	}
}

func TestDNSRequestMatchNamesWithDots(t *testing.T) {
	server := NewDNSServer(NewConfig())

	server.AddService("boo", Service{Name: "foo.boo", Image: "bar.zar"})
	server.AddService("baz", Service{Name: "baz", Image: "bar.zar"})
	server.AddService("abc", Service{Name: "bar", Image: "zar"})

	inputs := []struct {
		query, domain string
		expected      int
	}{
		{"foo.boo.bar.zar.docker", "docker", 2},
		{"zar.docker", "docker", 3},
		{"*.docker", "docker", 3},
		{"baz.bar.zar.docker", "docker", 2},
		{"boo.bar.zar.docker", "docker", 2},
		{"coo.bar.zar.docker", "docker", 1},
	}

	for _, input := range inputs {
		server.config.domain = NewDomain(input.domain)

		t.Log(input.query, input.domain)

		actual := 0
		for _ = range server.queryServices(input.query) {
			actual++
		}

		if actual != input.expected {
			t.Error(input, "Expected:", input.expected, "Got:", actual)
		}
	}
}

func TestGetExpandedId(t *testing.T) {
	server := NewDNSServer(NewConfig())

	server.AddService("416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df", Service{})
	server.AddService("316261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f14nothex", Service{})
	server.AddService("abcdefabcdef", Service{})

	inputs := map[string]string{
		"416":          "416",
		"41626":        "416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df",
		"416261e74515": "416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df",
		"31626":        "31626",
		"abcde":        "abcde",
		"foobar":       "foobar",
	}

	for input, expected := range inputs {
		if actual := server.getExpandedId(input); actual != expected {
			t.Error(input, "Expected:", expected, "Got:", actual)
		}
	}

}
<commit_msg>more test<commit_after>package main

import (
	"net"
	"testing"
	"time"

	"github.com/miekg/dns"
)

func TestDNSResponse(t *testing.T) {
	const TEST_ADDR = "127.0.0.1:9953"

	config := NewConfig()
	config.dnsAddr = TEST_ADDR

	server := NewDNSServer(config)
	go server.Start()

	// Allow some time for server to start
	time.Sleep(250 * time.Millisecond)

	m := new(dns.Msg)
	m.Id = dns.Id()
	m.RecursionDesired = true
	m.Question = []dns.Question{
		dns.Question{"google.com.", dns.TypeA, dns.ClassINET},
	}
	c := new(dns.Client)
	in, _, err := c.Exchange(m, TEST_ADDR)

	if err != nil {
		t.Error("Error response from the server", err)
	}

	if len(in.Answer) < 3 {
		t.Error("DNS request only responded ", len(in.Answer), "answers")
	}

	server.AddService("foo", Service{Name: "foo", Image: "bar", Ip: net.ParseIP("127.0.0.1")})
	server.AddService("baz", Service{Name: "baz", Image: "bar", Ip: net.ParseIP("127.0.0.1"), Ttl: -1, Alias: "www.seznam.cz"})

	server.AddService("erw", Service{Name: "erw", Image: "bar", Ip: net.ParseIP("127.0.0.1"), Ttl: -1, Alias: "www.seznam.cz"})

	var inputs = []struct {
		query    string
		expected int
	}{
		{"docker.", 3},
		{"*.docker.", 3},
		{"bar.docker.", 3},
		{"foo.docker.", 0},
		{"baz.bar.docker.", 1},
		{"www.seznam.cz.", 2},
	}

	for _, input := range inputs {
		t.Log(input.query)
		m := new(dns.Msg)
		m.Id = dns.Id()
		m.RecursionDesired = true
		m.Question = []dns.Question{
			dns.Question{input.query, dns.TypeA, dns.ClassINET},
		}
		c := new(dns.Client)
		in, _, err := c.Exchange(m, TEST_ADDR)
		if err != nil {
			t.Error("Error response from the server", err)
			break
		}
		if len(in.Answer) == 0 {
			t.Error(input, "No SOA anwer")
		}
		if _, ok := in.Answer[0].(*dns.SOA); ok {
			if input.expected != 0 {
				t.Error(input, "Expected:", input.expected, " Got:", 0)
			}
		} else if len(in.Answer) != input.expected {
			t.Error(input, "Expected:", input.expected, " Got:", len(in.Answer))
		}
	}

	// // This test is slow and pointless
	// server.Stop()
	//
	// c = new(dns.Client)
	// _, _, err = c.Exchange(m, TEST_ADDR)
	//
	// if err == nil {
	// 	t.Error("Server still running but should be shut down.")
	// }
}

func TestServiceManagement(t *testing.T) {
	list := ServiceListProvider(NewDNSServer(NewConfig()))

	if len(list.GetAllServices()) != 0 {
		t.Error("Initial service count should be 0.")
	}

	A := Service{Name: "bar"}
	list.AddService("foo", A)

	if len(list.GetAllServices()) != 1 {
		t.Error("Service count should be 1.")
	}

	A.Name = "baz"

	s1, err := list.GetService("foo")
	if err != nil {
		t.Error("GetService error", err)
	}

	if s1.Name != "bar" {
		t.Error("Expected: bar got:", s1.Name)
	}

	_, err = list.GetService("boo")

	if err == nil {
		t.Error("Request to boo should have failed")
	}

	list.AddService("boo", Service{Name: "boo"})

	all := list.GetAllServices()

	delete(all, "foo")
	s2 := all["boo"]
	s2.Name = "zoo"

	if len(list.GetAllServices()) != 2 {
		t.Error("Local map change should not remove items")
	}

	if s1, _ = list.GetService("boo"); s1.Name != "boo" {
		t.Error("Local map change should not change items")
	}

	err = list.RemoveService("bar")
	if err == nil {
		t.Error("Removing bar should fail")
	}

	err = list.RemoveService("foo")
	if err != nil {
		t.Error("Removing foo failed", err)
	}

	if len(list.GetAllServices()) != 1 {
		t.Error("Item count after remove should be 1")
	}

	list.AddService("416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df", Service{Name: "mysql"})

	if s1, _ = list.GetService("416261"); s1.Name != "mysql" {
		t.Error("Container can't be found by prefix")
	}

	err = list.RemoveService("416261")
	if err != nil {
		t.Error("Removing 416261 failed", err)
	}

	if len(list.GetAllServices()) != 1 {
		t.Error("Item count after remove should be 1")
	}

}

func TestAliasManagement(t *testing.T) {
	servId := "fsdfsdfsd"
	servId2 := "fdsfsdfsdsdfsdfsd"
	alias := "www.seznam.cz"
	s := NewDNSServer(NewConfig())
	s.AddService(servId, Service{Name: "mysql", Alias: alias})
	id_map, exists := s.aliases[alias]
	if len(id_map) != 1 {
		t.Error("should be only one alias")
	}
	_, id_is_there := id_map[servId]
	if !id_is_there {
		t.Error("alias registered under wrong id")
	}
	if !exists {
		t.Error("alias was not created")
	}
	s.AddService(servId2, Service{Name: "redis", Alias: alias})
	id_map, exists = s.aliases[alias]
	if len(id_map) != 2 {
		t.Error("should be two aliases now")
	}

	s.RemoveService(servId)

	id_map, exists = s.aliases[alias]
	if len(id_map) != 1 {
		t.Error("should be one alias again")
	}
	_, id_is_there = id_map[servId2]
	if !id_is_there {
		t.Error("alias registered under wrong id")
	}

	s.RemoveService(servId2)
	if len(s.aliases) != 0 {
		t.Error("aliases map should be empty")
	}

}

func TestDNSRequestMatch(t *testing.T) {
	server := NewDNSServer(NewConfig())

	server.AddService("foo", Service{Name: "foo", Image: "bar"})
	server.AddService("baz", Service{Name: "baz", Image: "bar"})
	server.AddService("abc", Service{Name: "def", Image: "ghi"})

	inputs := []struct {
		query, domain string
		expected      int
	}{
		{"docker", "docker", 3},
		{"baz.docker", "docker.local", 0},
		{"docker.local", "docker.local", 3},
		{"foo.docker.local", "docker.local", 0},
		{"bar.docker.local", "docker.local", 2},
		{"foo.bar.docker.local", "docker.local", 1},
		{"*.local", "docker.local", 3},
		{"*.docker.local", "docker.local", 3},
		{"bar.*.local", "docker.local", 2},
		{"*.*.local", "docker.local", 3},
		{"foo.*.local", "docker.local", 0},
		{"bar.*.docker.local", "docker.local", 0},
		{"foo.*.docker", "docker", 1},
		{"baz.foo.bar.docker.local", "docker.local", 1},
		{"foo.bar", "baz.foo.bar", 3},
	}

	for _, input := range inputs {
		server.config.domain = NewDomain(input.domain)

		t.Log(input.query, input.domain)

		actual := 0
		for _ = range server.queryServices(input.query) {
			actual++
		}

		if actual != input.expected {
			t.Error(input, "Expected:", input.expected, "Got:", actual)
		}
	}
}

func TestDNSRequestMatchNamesWithDots(t *testing.T) {
	server := NewDNSServer(NewConfig())

	server.AddService("boo", Service{Name: "foo.boo", Image: "bar.zar"})
	server.AddService("baz", Service{Name: "baz", Image: "bar.zar"})
	server.AddService("abc", Service{Name: "bar", Image: "zar"})

	inputs := []struct {
		query, domain string
		expected      int
	}{
		{"foo.boo.bar.zar.docker", "docker", 2},
		{"zar.docker", "docker", 3},
		{"*.docker", "docker", 3},
		{"baz.bar.zar.docker", "docker", 2},
		{"boo.bar.zar.docker", "docker", 2},
		{"coo.bar.zar.docker", "docker", 1},
	}

	for _, input := range inputs {
		server.config.domain = NewDomain(input.domain)

		t.Log(input.query, input.domain)

		actual := 0
		for _ = range server.queryServices(input.query) {
			actual++
		}

		if actual != input.expected {
			t.Error(input, "Expected:", input.expected, "Got:", actual)
		}
	}
}

func TestGetExpandedId(t *testing.T) {
	server := NewDNSServer(NewConfig())

	server.AddService("416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df", Service{})
	server.AddService("316261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f14nothex", Service{})
	server.AddService("abcdefabcdef", Service{})

	inputs := map[string]string{
		"416":          "416",
		"41626":        "416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df",
		"416261e74515": "416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df",
		"31626":        "31626",
		"abcde":        "abcde",
		"foobar":       "foobar",
	}

	for input, expected := range inputs {
		if actual := server.getExpandedId(input); actual != expected {
			t.Error(input, "Expected:", expected, "Got:", actual)
		}
	}

}
<|endoftext|>


<commit_before><commit_msg>use Mutex in evaluator state<commit_after><|endoftext|>


<commit_before>package application

import (
	"encoding/json"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/stretchr/testify/assert"
	"github.com/thoas/gokvstores"
	"github.com/thoas/picfit/dummy"
	"github.com/thoas/picfit/engines"
	"io/ioutil"
	"mime"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"strings"
	"testing"
	"time"
)

type Dimension struct {
	Width  int
	Height int
}

type TestRequest struct {
	URL         string
	Dimensions  *Dimension
	ContentType string
}

func newDummyApplication() *Application {
	app := NewApplication()
	app.SourceStorage = &dummy.DummyStorage{}
	app.DestStorage = &dummy.DummyStorage{}
	app.KVStore = &dummy.DummyKVStore{}
	app.Engine = &engines.GoImageEngine{}

	return app
}

func newHTTPServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "GET" {
			f, err := os.Open(path.Join("testdata", r.URL.Path))
			defer f.Close()

			if err != nil {
				w.WriteHeader(500)
			} else {
				bytes, _ := ioutil.ReadAll(f)

				contentType := mime.TypeByExtension(path.Ext(r.URL.Path))

				w.WriteHeader(200)

				w.Header().Set("Content-Type", contentType)
				w.Write(bytes)
			}
		}
	}))
}

func TestStorageApplicationWithPath(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()
	defer ts.CloseClientConnections()

	tmp := os.TempDir()

	f, err := os.Open("testdata/avatar.png")
	assert.Nil(t, err)
	defer f.Close()

	body, err := ioutil.ReadAll(f)
	assert.Nil(t, err)

	// We store the image at the tmp location to access it
	// with the SourceStorage
	ioutil.WriteFile(path.Join(tmp, "avatar.png"), body, 0755)

	content := `{
	  "debug": true,
	  "port": 3001,
	  "kvstore": {
		"prefix": "picfit:",
		"type": "redis",
		"host": "127.0.0.1",
		"db": 0,
		"password": "",
		"port": 6379
	  },
	  "storage": {
		"src": {
		  "type": "fs",
		  "location": "%s",
		  "base_url": "http://img.example.com"
		}
	  },
	  "shard": {
		"width": 1,
		"depth": 2
	  }
	}`

	content = fmt.Sprintf(content, tmp)

	app, err := NewFromConfig(content)
	assert.Nil(t, err)

	negroni := app.InitRouter()

	connection := app.KVStore.Connection()
	defer connection.Close()

	location := "http://example.com/display/resize/100x100/avatar.png"

	request, _ := http.NewRequest("GET", location, nil)

	res := httptest.NewRecorder()

	negroni.ServeHTTP(res, request)

	assert.Equal(t, 200, res.Code)

	// We wait until the goroutine to save the file on disk is finished
	timer1 := time.NewTimer(time.Second * 2)
	<-timer1.C

	etag := res.Header().Get("ETag")

	key := app.WithPrefix(etag)

	assert.True(t, connection.Exists(key))

	filepath, _ := gokvstores.String(connection.Get(key))

	parts := strings.Split(filepath, "/")

	assert.Equal(t, len(parts), 3)
	assert.Equal(t, len(parts[0]), 1)
	assert.Equal(t, len(parts[1]), 1)

	assert.True(t, app.SourceStorage.Exists(filepath))

	location = "http://example.com/get/resize/100x100/avatar.png"

	request, _ = http.NewRequest("GET", location, nil)

	res = httptest.NewRecorder()

	negroni.ServeHTTP(res, request)

	assert.Equal(t, 200, res.Code)
	assert.Equal(t, "application/json", res.Header().Get("Content-Type"))

	var dat map[string]interface{}

	err = json.Unmarshal(res.Body.Bytes(), &dat)

	assert.Nil(t, err)

	expected := "http://img.example.com/" + filepath

	assert.Equal(t, expected, dat["url"].(string))

	location = "http://example.com/redirect/resize/100x100/avatar.png"

	request, _ = http.NewRequest("GET", location, nil)

	res = httptest.NewRecorder()

	negroni.ServeHTTP(res, request)

	assert.Equal(t, expected, res.Header().Get("Location"))
	assert.Equal(t, 301, res.Code)
}

func TestStorageApplicationWithURL(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()
	defer ts.CloseClientConnections()

	tmp := os.TempDir()

	content := `{
	  "debug": true,
	  "port": 3001,
	  "kvstore": {
		"prefix": "picfit:",
		"type": "redis",
		"host": "127.0.0.1",
		"db": 0,
		"password": "",
		"port": 6379
	  },
	  "storage": {
		"src": {
		  "type": "fs",
		  "location": "%s"
		}
	  }
	}`

	content = fmt.Sprintf(content, tmp)

	app, err := NewFromConfig(content)
	assert.Nil(t, err)

	connection := app.KVStore.Connection()
	defer connection.Close()

	assert.NotNil(t, app.SourceStorage)
	assert.Equal(t, app.SourceStorage, app.DestStorage)

	filename := "avatar.png"

	u, _ := url.Parse(ts.URL + "/" + filename)

	location := fmt.Sprintf("http://example.com/display?url=%s&w=100&h=100&op=resize", u.String())

	request, _ := http.NewRequest("GET", location, nil)

	res := httptest.NewRecorder()

	handler := app.ServeHTTP(ImageHandler)

	handler.ServeHTTP(res, request)

	assert.Equal(t, res.Code, 200)

	// We wait until the goroutine to save the file on disk is finished
	timer1 := time.NewTimer(time.Second * 2)
	<-timer1.C

	etag := res.Header().Get("ETag")

	key := app.WithPrefix(etag)

	assert.True(t, connection.Exists(key))

	filepath, _ := gokvstores.String(connection.Get(key))

	parts := strings.Split(filepath, "/")

	assert.Equal(t, len(parts), 1)

	assert.True(t, app.SourceStorage.Exists(filepath))
}

func TestDummyApplication(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()
	defer ts.CloseClientConnections()

	app := newDummyApplication()

	for _, filename := range []string{"avatar.png", "schwarzy.jpg", "giphy.gif"} {
		u, _ := url.Parse(ts.URL + "/" + filename)

		contentType := mime.TypeByExtension(path.Ext(filename))

		tests := []*TestRequest{
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=100&h=30&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  100,
					Height: 30,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail&fmt=jpg", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
				ContentType: "image/jpeg",
			},
		}

		for _, test := range tests {
			request, _ := http.NewRequest("GET", test.URL, nil)

			res := httptest.NewRecorder()

			handler := app.ServeHTTP(ImageHandler)

			handler.ServeHTTP(res, request)

			img, err := imaging.Decode(res.Body)

			assert.Nil(t, err)

			if test.ContentType != "" {
				assert.Equal(t, res.Header().Get("Content-Type"), test.ContentType)
			} else {
				assert.Equal(t, res.Header().Get("Content-Type"), contentType)
			}

			assert.Equal(t, res.Code, 200)

			if img.Bounds().Max.X != test.Dimensions.Width {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.X, test.Dimensions.Width)
			}

			if img.Bounds().Max.Y != test.Dimensions.Height {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.Y, test.Dimensions.Height)
			}
		}
	}
}
<commit_msg>Add a test case when deadling with errors<commit_after>package application

import (
	"encoding/json"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/stretchr/testify/assert"
	"github.com/thoas/gokvstores"
	"github.com/thoas/picfit/dummy"
	"github.com/thoas/picfit/engines"
	"io/ioutil"
	"mime"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"strings"
	"testing"
	"time"
)

type Dimension struct {
	Width  int
	Height int
}

type TestRequest struct {
	URL         string
	Dimensions  *Dimension
	ContentType string
}

func newDummyApplication() *Application {
	app := NewApplication()
	app.SourceStorage = &dummy.DummyStorage{}
	app.DestStorage = &dummy.DummyStorage{}
	app.KVStore = &dummy.DummyKVStore{}
	app.Engine = &engines.GoImageEngine{}

	return app
}

func newHTTPServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "GET" {
			f, err := os.Open(path.Join("testdata", r.URL.Path))
			defer f.Close()

			if err != nil {
				w.WriteHeader(500)
			} else {
				bytes, _ := ioutil.ReadAll(f)

				contentType := mime.TypeByExtension(path.Ext(r.URL.Path))

				w.WriteHeader(200)

				w.Header().Set("Content-Type", contentType)
				w.Write(bytes)
			}
		}
	}))
}

func TestStorageApplicationWithPath(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()
	defer ts.CloseClientConnections()

	tmp := os.TempDir()

	f, err := os.Open("testdata/avatar.png")
	assert.Nil(t, err)
	defer f.Close()

	body, err := ioutil.ReadAll(f)
	assert.Nil(t, err)

	// We store the image at the tmp location to access it
	// with the SourceStorage
	ioutil.WriteFile(path.Join(tmp, "avatar.png"), body, 0755)

	content := `{
	  "debug": true,
	  "port": 3001,
	  "kvstore": {
		"prefix": "picfit:",
		"type": "redis",
		"host": "127.0.0.1",
		"db": 0,
		"password": "",
		"port": 6379
	  },
	  "storage": {
		"src": {
		  "type": "fs",
		  "location": "%s",
		  "base_url": "http://img.example.com"
		}
	  },
	  "shard": {
		"width": 1,
		"depth": 2
	  }
	}`

	content = fmt.Sprintf(content, tmp)

	app, err := NewFromConfig(content)
	assert.Nil(t, err)

	negroni := app.InitRouter()

	connection := app.KVStore.Connection()
	defer connection.Close()

	location := "http://example.com/display/resize/100x100/avatar.png"

	request, _ := http.NewRequest("GET", location, nil)

	res := httptest.NewRecorder()

	negroni.ServeHTTP(res, request)

	assert.Equal(t, 200, res.Code)

	// We wait until the goroutine to save the file on disk is finished
	timer1 := time.NewTimer(time.Second * 2)
	<-timer1.C

	etag := res.Header().Get("ETag")

	key := app.WithPrefix(etag)

	assert.True(t, connection.Exists(key))

	filepath, _ := gokvstores.String(connection.Get(key))

	parts := strings.Split(filepath, "/")

	assert.Equal(t, len(parts), 3)
	assert.Equal(t, len(parts[0]), 1)
	assert.Equal(t, len(parts[1]), 1)

	assert.True(t, app.SourceStorage.Exists(filepath))

	location = "http://example.com/get/resize/100x100/avatar.png"

	request, _ = http.NewRequest("GET", location, nil)

	res = httptest.NewRecorder()

	negroni.ServeHTTP(res, request)

	assert.Equal(t, 200, res.Code)
	assert.Equal(t, "application/json", res.Header().Get("Content-Type"))

	var dat map[string]interface{}

	err = json.Unmarshal(res.Body.Bytes(), &dat)

	assert.Nil(t, err)

	expected := "http://img.example.com/" + filepath

	assert.Equal(t, expected, dat["url"].(string))

	location = "http://example.com/redirect/resize/100x100/avatar.png"

	request, _ = http.NewRequest("GET", location, nil)

	res = httptest.NewRecorder()

	negroni.ServeHTTP(res, request)

	assert.Equal(t, expected, res.Header().Get("Location"))
	assert.Equal(t, 301, res.Code)
}

func TestStorageApplicationWithURL(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()
	defer ts.CloseClientConnections()

	tmp := os.TempDir()

	content := `{
	  "debug": true,
	  "port": 3001,
	  "kvstore": {
		"prefix": "picfit:",
		"type": "redis",
		"host": "127.0.0.1",
		"db": 0,
		"password": "",
		"port": 6379
	  },
	  "storage": {
		"src": {
		  "type": "fs",
		  "location": "%s"
		}
	  }
	}`

	content = fmt.Sprintf(content, tmp)

	app, err := NewFromConfig(content)
	assert.Nil(t, err)

	connection := app.KVStore.Connection()
	defer connection.Close()

	assert.NotNil(t, app.SourceStorage)
	assert.Equal(t, app.SourceStorage, app.DestStorage)

	filename := "avatar.png"

	u, _ := url.Parse(ts.URL + "/" + filename)

	location := fmt.Sprintf("http://example.com/display?url=%s&w=100&h=100&op=resize", u.String())

	request, _ := http.NewRequest("GET", location, nil)

	res := httptest.NewRecorder()

	handler := app.ServeHTTP(ImageHandler)

	handler.ServeHTTP(res, request)

	assert.Equal(t, res.Code, 200)

	// We wait until the goroutine to save the file on disk is finished
	timer1 := time.NewTimer(time.Second * 2)
	<-timer1.C

	etag := res.Header().Get("ETag")

	key := app.WithPrefix(etag)

	assert.True(t, connection.Exists(key))

	filepath, _ := gokvstores.String(connection.Get(key))

	parts := strings.Split(filepath, "/")

	assert.Equal(t, len(parts), 1)

	assert.True(t, app.SourceStorage.Exists(filepath))
}

func TestDummyApplicationErrors(t *testing.T) {
	app := newDummyApplication()

	location := "http://example.com/display/resize/100x100/avatar.png"

	request, _ := http.NewRequest("GET", location, nil)

	res := httptest.NewRecorder()

	handler := app.ServeHTTP(ImageHandler)

	handler.ServeHTTP(res, request)
	assert.Equal(t, 400, res.Code)
}

func TestDummyApplication(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()
	defer ts.CloseClientConnections()

	app := newDummyApplication()

	for _, filename := range []string{"avatar.png", "schwarzy.jpg", "giphy.gif"} {
		u, _ := url.Parse(ts.URL + "/" + filename)

		contentType := mime.TypeByExtension(path.Ext(filename))

		tests := []*TestRequest{
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=100&h=30&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  100,
					Height: 30,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail&fmt=jpg", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
				ContentType: "image/jpeg",
			},
		}

		for _, test := range tests {
			request, _ := http.NewRequest("GET", test.URL, nil)

			res := httptest.NewRecorder()

			handler := app.ServeHTTP(ImageHandler)

			handler.ServeHTTP(res, request)

			img, err := imaging.Decode(res.Body)

			assert.Nil(t, err)

			if test.ContentType != "" {
				assert.Equal(t, res.Header().Get("Content-Type"), test.ContentType)
			} else {
				assert.Equal(t, res.Header().Get("Content-Type"), contentType)
			}

			assert.Equal(t, res.Code, 200)

			if img.Bounds().Max.X != test.Dimensions.Width {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.X, test.Dimensions.Width)
			}

			if img.Bounds().Max.Y != test.Dimensions.Height {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.Y, test.Dimensions.Height)
			}
		}
	}
}
<|endoftext|>