<commit_before>package bot

import "bufio"
import "encoding/json"
import "fmt"
import "net/http"
import "regexp"
// import "runtime"
import "sort"
import "strings"
// import "time"
import "github.com/sgt-kabukiman/kabukibot/twitch"

type EmoteManager interface {
	FindEmotesInMessage(msg twitch.TextMessage) emoteList
	UpdateEmotes() error
}

type emoteList []string

type emoteManager struct {
	emotes struct {
		global       emoteList
		subscribers  map[int]*emoteList
		frankerfacez map[string]*emoteList
	}
	regexes struct {
		channels    map[string]*regexp.Regexp
		subscribers map[string]*regexp.Regexp
	}
}

type twitchEmoticons struct {
	Emoticons []struct {
		Regex  string
		Images []struct {
			EmoticonSet *int `json:"emoticon_set"` // use a pointer because this can be null
		}
	}
}

func NewEmoteManager() EmoteManager {
	em := emoteManager{}
	em.reset()

	return &em
}

func (self *emoteManager) reset() {
	self.emotes.subscribers  = make(map[int]*emoteList)
	self.emotes.frankerfacez = make(map[string]*emoteList)

	self.regexes.channels    = make(map[string]*regexp.Regexp)
	self.regexes.subscribers = make(map[string]*regexp.Regexp)
}

func (self *emoteManager) FindEmotesInMessage(msg twitch.TextMessage) emoteList {
	// no emotes fetched yet
	if len(self.emotes.global) == 0 || len(msg.Text()) < 3 {
		return make(emoteList, 0)
	}

	chanName := msg.Channel().Name

	// Search for any channel-global emotes
	chanRegex, exists := self.regexes.channels[chanName]
	if !exists {
		chanRegex = self.buildChannelRegex(chanName)
		self.regexes.channels[chanName] = chanRegex
	}

	var result emoteList

	emotes := chanRegex.FindAllString(msg.Text(), -1)
	if emotes != nil {
		result = emotes
	}

	// If the user is a subscriber to any channel, as also search for his
	// subscriber emotes. In order to minimize the memory footprint, we are
	// storing the regexes per emotesets (JSON-encoded) value, so if two or
	// more users are subscribed to the same channels, they will both use
	// the same regex object.

	set := msg.User().EmoteSet

	if len(set) > 0 {
		sort.Ints(set)

		setKey := fmt.Sprintf("%v", set)

		// Search for any channel-global emotes
		setRegex, exists := self.regexes.subscribers[setKey]
		if !exists {
			setRegex = self.buildEmoteSetRegex(set)
			self.regexes.subscribers[setKey] = setRegex
		}

		if setRegex != nil {
			emotes = setRegex.FindAllString(msg.Text(), -1)

			if emotes != nil {
				for _, emote := range emotes {
					result = append(result, emote)
				}
			}
		}
	}

	return result
}

func (self *emoteManager) buildChannelRegex(channel string) *regexp.Regexp {
	// calculate initial capacity

	capacity := len(self.emotes.global)

	ffzGlobalEmotes, hasFfzGlobal := self.emotes.frankerfacez["global"]
	if hasFfzGlobal {
		capacity += len(*ffzGlobalEmotes)
	}

	ffzChanEmotes, hasFfzChan := self.emotes.frankerfacez[channel]
	if hasFfzChan {
		capacity += len(*ffzChanEmotes)
	}

	codes := make([]string, 0, capacity)

	// collect all relevant, user-independent emotes

	for _, code := range self.emotes.global {
		codes = append(codes, code)
	}

	if hasFfzGlobal {
		for _, code := range *ffzGlobalEmotes {
			codes = append(codes, code)
		}
	}

	if hasFfzChan {
		for _, code := range *ffzChanEmotes {
			codes = append(codes, code)
		}
	}

	// build the regex

	expression := fmt.Sprintf(`\b(%s)\b`, strings.Join(codes, "|"))

	return regexp.MustCompile(expression)
}

func (self *emoteManager) buildEmoteSetRegex(set []int) *regexp.Regexp {
	codes := make([]string, 0)

	for _, setID := range set {
		emotes, exists := self.emotes.subscribers[setID]
		if exists {
			for _, emote := range *emotes {
				codes = append(codes, emote)
			}
		}
	}

	if len(codes) == 0 {
		return nil
	}

	expression := fmt.Sprintf(`\b(%s)\b`, strings.Join(codes, "|"))

	return regexp.MustCompile(expression)
}

func (self *emoteManager) UpdateEmotes() error {
	// reset()

	err := self.updateTwitchEmotes()
	if err != nil {
		return err
	}

	// stamp("fetched twitch emotes")

	err = self.updateFrankerFaceZEmotes()

	// stamp("fetched ffz emotes")

	return err
}

func (self *emoteManager) updateTwitchEmotes() error {
	// fetch URL
	response, err := http.Get("https://api.twitch.tv/kraken/chat/emoticons")
	if err != nil {
		return fmt.Errorf("Could not fetch Twitch emotes: %s", err.Error())
	}

	// parse JSON
	emoteData := twitchEmoticons{}

	if json.NewDecoder(response.Body).Decode(&emoteData) != nil {
		return fmt.Errorf("Received invalid JSON for Twitch emotes.")
	}

	isIrregular := regexp.MustCompile(`[?$\[\]()\\;]`)

	self.reset()

	for _, emoteStruct := range emoteData.Emoticons {
		emoteCode := emoteStruct.Regex

		if isIrregular.MatchString(emoteCode) {
			continue
		}

		for _, image := range emoteStruct.Images {
			emoteSetID := image.EmoticonSet

			var list *emoteList

			if emoteSetID == nil {
				list = &self.emotes.global
			} else {
				l, exists := self.emotes.subscribers[*emoteSetID]
				if exists {
					list = l
				} else {
					newList := make(emoteList, 0)

					self.emotes.subscribers[*emoteSetID] = &newList
					list = &newList
				}
			}

			*list = append(*list, emoteCode)
		}
	}

	return nil
}

func (self *emoteManager) updateFrankerFaceZEmotes() error {
	// fetch URL
	response, err := http.Get("http://frankerfacez.com/users.txt")
	if err != nil {
		return fmt.Errorf("Could not fetch FFZ emotes: %s", err.Error())
	}

	self.emotes.frankerfacez = make(map[string]*emoteList)

	// read text file line by line
	defer response.Body.Close()

	scanner  := bufio.NewScanner(response.Body)
	lastChan := ""

	for scanner.Scan() {
		line := scanner.Text()

		if string(line[0]) == "." {
			*self.emotes.frankerfacez[lastChan] = append(*self.emotes.frankerfacez[lastChan], line[1:])
		} else {
			_, exists := self.emotes.frankerfacez[line]
			if !exists {
				newList := make(emoteList, 0)
				self.emotes.frankerfacez[line] = &newList
			}

			lastChan = line
		}
	}

	if err := scanner.Err(); err != nil {
		return err
	}

	return nil
}

// var last = time.Now()

// func stamp(name string) {
// 	now  := time.Now()
// 	diff := now.Sub(last)

// 	last = now

// 	var mem runtime.MemStats
// 	runtime.ReadMemStats(&mem)

// 	fmt.Printf("%s: %s (res = %.3f MiB, alloc = %.3f MiB, total alloc = %.3f MiB)\n", name, diff.String(), float64(mem.Sys) / (1024*1024), float64(mem.Alloc) / (1024*1024), float64(mem.TotalAlloc) / (1024*1024))
// }

// func reset() {
// 	last = time.Now()
// 	stamp("RESET")
// }
<commit_msg>attempt to replace the brute-force updating of emotes by a simple merging algorithm<commit_after>package bot

import "bufio"
import "encoding/json"
import "fmt"
import "net/http"
import "regexp"
// import "runtime"
import "sort"
import "strings"
// import "time"
import "github.com/sgt-kabukiman/kabukibot/twitch"

type EmoteManager interface {
	FindEmotesInMessage(msg twitch.TextMessage) emoteList
	UpdateEmotes() error
}

type emoteList []string

func (self *emoteList) sync(other emoteList) {
	// removes emotes no longer available

	for i := len(*self)-1; i >= 0; i-- {
		found := false
		emote := (*self)[i]

		for _, e := range other {
			if e == emote {
				found = true
				break
			}
		}

		if !found {
			*self = append((*self)[:i], (*self)[(i+1):]...)
		}
	}

	// look for newly added emotes

	for _, emote := range other {
		found := false

		for _, e := range *self {
			if e == emote {
				found = true
				break
			}
		}

		if !found {
			*self = append(*self, emote)
		}
	}
}

type emoteManager struct {
	emotes struct {
		global       emoteList
		subscribers  map[int]*emoteList
		frankerfacez map[string]*emoteList
	}
	regexes struct {
		channels    map[string]*regexp.Regexp
		subscribers map[string]*regexp.Regexp
	}
}

type twitchEmoticons struct {
	Emoticons []struct {
		Regex  string
		Images []struct {
			EmoticonSet *int `json:"emoticon_set"` // use a pointer because this can be null
		}
	}
}

func NewEmoteManager() EmoteManager {
	em := emoteManager{}
	em.emotes.subscribers  = make(map[int]*emoteList)
	em.emotes.frankerfacez = make(map[string]*emoteList)

	em.reset()

	return &em
}

func (self *emoteManager) reset() {
	self.regexes.channels    = make(map[string]*regexp.Regexp)
	self.regexes.subscribers = make(map[string]*regexp.Regexp)
}

func (self *emoteManager) FindEmotesInMessage(msg twitch.TextMessage) emoteList {
	// no emotes fetched yet
	if len(self.emotes.global) == 0 || len(msg.Text()) < 3 {
		return make(emoteList, 0)
	}

	chanName := msg.Channel().Name

	// Search for any channel-global emotes
	chanRegex, exists := self.regexes.channels[chanName]
	if !exists {
		chanRegex = self.buildChannelRegex(chanName)
		self.regexes.channels[chanName] = chanRegex
	}

	var result emoteList

	emotes := chanRegex.FindAllString(msg.Text(), -1)
	if emotes != nil {
		result = emotes
	}

	// If the user is a subscriber to any channel, as also search for his
	// subscriber emotes. In order to minimize the memory footprint, we are
	// storing the regexes per emotesets (JSON-encoded) value, so if two or
	// more users are subscribed to the same channels, they will both use
	// the same regex object.

	set := msg.User().EmoteSet

	if len(set) > 0 {
		sort.Ints(set)

		setKey := fmt.Sprintf("%v", set)

		// Search for any channel-global emotes
		setRegex, exists := self.regexes.subscribers[setKey]
		if !exists {
			setRegex = self.buildEmoteSetRegex(set)
			self.regexes.subscribers[setKey] = setRegex
		}

		if setRegex != nil {
			emotes = setRegex.FindAllString(msg.Text(), -1)

			if emotes != nil {
				for _, emote := range emotes {
					result = append(result, emote)
				}
			}
		}
	}

	return result
}

func (self *emoteManager) buildChannelRegex(channel string) *regexp.Regexp {
	// calculate initial capacity

	capacity := len(self.emotes.global)

	ffzGlobalEmotes, hasFfzGlobal := self.emotes.frankerfacez["global"]
	if hasFfzGlobal {
		capacity += len(*ffzGlobalEmotes)
	}

	ffzChanEmotes, hasFfzChan := self.emotes.frankerfacez[channel]
	if hasFfzChan {
		capacity += len(*ffzChanEmotes)
	}

	codes := make([]string, 0, capacity)

	// collect all relevant, user-independent emotes

	for _, code := range self.emotes.global {
		codes = append(codes, code)
	}

	if hasFfzGlobal {
		for _, code := range *ffzGlobalEmotes {
			codes = append(codes, code)
		}
	}

	if hasFfzChan {
		for _, code := range *ffzChanEmotes {
			codes = append(codes, code)
		}
	}

	// build the regex

	expression := fmt.Sprintf(`\b(%s)\b`, strings.Join(codes, "|"))

	return regexp.MustCompile(expression)
}

func (self *emoteManager) buildEmoteSetRegex(set []int) *regexp.Regexp {
	codes := make([]string, 0)

	for _, setID := range set {
		emotes, exists := self.emotes.subscribers[setID]
		if exists {
			for _, emote := range *emotes {
				codes = append(codes, emote)
			}
		}
	}

	if len(codes) == 0 {
		return nil
	}

	expression := fmt.Sprintf(`\b(%s)\b`, strings.Join(codes, "|"))

	return regexp.MustCompile(expression)
}

func (self *emoteManager) UpdateEmotes() error {
	// reset()

	err := self.updateTwitchEmotes()
	if err != nil {
		return err
	}

	// stamp("fetched twitch emotes")

	err = self.updateFrankerFaceZEmotes()

	// stamp("fetched ffz emotes")

	self.reset()

	return err
}

func (self *emoteManager) updateTwitchEmotes() error {
	// fetch URL
	response, err := http.Get("https://api.twitch.tv/kraken/chat/emoticons")
	if err != nil {
		return fmt.Errorf("Could not fetch Twitch emotes: %s", err.Error())
	}

	// parse JSON
	emoteData := twitchEmoticons{}

	if json.NewDecoder(response.Body).Decode(&emoteData) != nil {
		return fmt.Errorf("Received invalid JSON for Twitch emotes.")
	}

	// Build up a structure matching the one stored in self, so we can then
	// compare and up the version in self accordingly. The newly constructed
	// struct is then discarded.

	isIrregular    := regexp.MustCompile(`[?$\[\]()\\;]`)
	newGlobals     := make(emoteList, 0, len(self.emotes.global))
	newSubscribers := make(map[int]*emoteList)

	for _, emoteStruct := range emoteData.Emoticons {
		emoteCode := emoteStruct.Regex

		if isIrregular.MatchString(emoteCode) {
			continue
		}

		for _, image := range emoteStruct.Images {
			emoteSetID := image.EmoticonSet

			var list *emoteList

			if emoteSetID == nil {
				list = &newGlobals
			} else {
				l, exists := newSubscribers[*emoteSetID]
				if exists {
					list = l
				} else {
					capacity := 0
					oldList, hasOldList := self.emotes.subscribers[*emoteSetID]
					if hasOldList {
						capacity = len(*oldList)
					}

					newList := make(emoteList, 0, capacity)

					newSubscribers[*emoteSetID] = &newList
					list = &newList
				}
			}

			*list = append(*list, emoteCode)
		}
	}

	// Now we can compare. First we look for removed elements.

	self.emotes.global.sync(newGlobals)

	// and now the same dance for subscriber emoticons

	for setID, emotes := range self.emotes.subscribers {
		newList, hasNewList := newSubscribers[setID]

		if !hasNewList {
			delete(self.emotes.subscribers, setID)
			continue
		}

		emotes.sync(*newList)
	}

	// look for newly added emote sets

	for setID, setList := range newSubscribers {
		_, exists := self.emotes.subscribers[setID]
		if !exists {
			self.emotes.subscribers[setID] = setList
			continue
		}
	}

	return nil
}

func (self *emoteManager) updateFrankerFaceZEmotes() error {
	// fetch URL
	response, err := http.Get("http://frankerfacez.com/users.txt")
	if err != nil {
		return fmt.Errorf("Could not fetch FFZ emotes: %s", err.Error())
	}

	// read text file line by line
	defer response.Body.Close()

	scanner   := bufio.NewScanner(response.Body)
	lastChan  := ""
	newEmotes := make(emoteList, 0)
	updated   := make([]string, 0, len(self.emotes.frankerfacez))

	for scanner.Scan() {
		line := scanner.Text()

		if string(line[0]) == "." {
			newEmotes = append(newEmotes, line[1:])
		} else {
			// we just completed reading a complete channel, so let's sync it
			if len(lastChan) > 0 {
				emotes, exists := self.emotes.frankerfacez[lastChan]

				if !exists {
					self.emotes.frankerfacez[lastChan] = &newEmotes
				} else {
					emotes.sync(newEmotes)
				}
			}

			lastChan  = line
			updated   = append(updated, line)
			newEmotes = make(emoteList, 0)
		}
	}

	if err := scanner.Err(); err != nil {
		return err
	}

	// syncup the last channel

	emotes, exists := self.emotes.frankerfacez[lastChan]

	if !exists {
		self.emotes.frankerfacez[lastChan] = &newEmotes
	} else {
		emotes.sync(newEmotes)
	}

	// look for removed channels

	for channel, _ := range self.emotes.frankerfacez {
		found := false

		for _, c := range updated {
			if c == channel {
				found = true
				break
			}
		}

		if !found {
			delete(self.emotes.frankerfacez, channel)
		}
	}

	return nil
}

// var last = time.Now()

// func stamp(name string) {
// 	now  := time.Now()
// 	diff := now.Sub(last)

// 	last = now

// 	var mem runtime.MemStats
// 	runtime.ReadMemStats(&mem)

// 	fmt.Printf("%s: %s (res = %.3f MiB, alloc = %.3f MiB, total alloc = %.3f MiB)\n", name, diff.String(), float64(mem.Sys) / (1024*1024), float64(mem.Alloc) / (1024*1024), float64(mem.TotalAlloc) / (1024*1024))
// }

// func reset() {
// 	last = time.Now()
// 	stamp("RESET")
// }
<|endoftext|>


<commit_before>package helix

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

	helixClient "github.com/nicklaw5/helix"
	log "github.com/sirupsen/logrus"
)

// Client wrapper for helix
type Client struct {
	clientID     string
	clientSecret string
	client       *helixClient.Client
	httpClient   *http.Client
}

var (
	userCacheByID       map[string]*UserData
	userCacheByUsername map[string]*UserData
)

func init() {
	userCacheByID = map[string]*UserData{}
	userCacheByUsername = map[string]*UserData{}
}

// NewClient Create helix client
func NewClient(clientID, clientSecret string) Client {
	client, err := helixClient.NewClient(&helixClient.Options{
		ClientID: clientID,
	})
	if err != nil {
		panic(err)
	}

	resp, err := client.RequestAppAccessToken([]string{})
	if err != nil {
		panic(err)
	}
	log.Infof("Requested access token, response: %d, expires in: %d", resp.StatusCode, resp.Data.ExpiresIn)
	client.SetAppAccessToken(resp.Data.AccessToken)

	return Client{
		clientID:     clientID,
		clientSecret: clientSecret,
		client:       client,
		httpClient:   &http.Client{},
	}
}

// StartRefreshTokenRoutine refresh our token
func (c *Client) StartRefreshTokenRoutine() {
	ticker := time.NewTicker(24 * time.Hour)

	for range ticker.C {
		resp, err := c.client.RequestAppAccessToken([]string{})
		if err != nil {
			log.Error(err)
			continue
		}
		log.Infof("Requested access token from routine, response: %d, expires in: %d", resp.StatusCode, resp.Data.ExpiresIn)

		c.client.SetAppAccessToken(resp.Data.AccessToken)
	}
}

type userResponse struct {
	Data []UserData `json:"data"`
}

// UserData exported data from twitch
type UserData struct {
	ID              string `json:"id"`
	Login           string `json:"login"`
	DisplayName     string `json:"display_name"`
	Type            string `json:"type"`
	BroadcasterType string `json:"broadcaster_type"`
	Description     string `json:"description"`
	ProfileImageURL string `json:"profile_image_url"`
	OfflineImageURL string `json:"offline_image_url"`
	ViewCount       int    `json:"view_count"`
	Email           string `json:"email"`
}

// GetUsersByUserIds receive userData for given ids
func (c *Client) GetUsersByUserIds(userIDs []string) (map[string]UserData, error) {
	var filteredUserIDs []string

	for _, id := range userIDs {
		if _, ok := userCacheByID[id]; !ok {
			filteredUserIDs = append(filteredUserIDs, id)
		}
	}

	if len(filteredUserIDs) > 0 {
		var chunks [][]string
		for i := 0; i < len(filteredUserIDs); i += 100 {
			end := i + 100

			if end > len(filteredUserIDs) {
				end = len(filteredUserIDs)
			}

			chunks = append(chunks, filteredUserIDs[i:end])
		}

		for _, chunk := range chunks {
			resp, err := c.client.GetUsers(&helixClient.UsersParams{
				IDs: chunk,
			})
			if err != nil {
				return make(map[string]UserData), err
			}
			log.Infof("[helix] %d GetUsersByUserIds %v", resp.StatusCode, chunk)
			if resp.StatusCode > http.StatusMultipleChoices {
				return make(map[string]UserData), fmt.Errorf("bad helix response: %v", resp.ErrorMessage)
			}

			for _, user := range resp.Data.Users {
				data := &UserData{
					ID:              user.ID,
					Login:           user.Login,
					DisplayName:     user.Login,
					Type:            user.Type,
					BroadcasterType: user.BroadcasterType,
					Description:     user.Description,
					ProfileImageURL: user.ProfileImageURL,
					OfflineImageURL: user.OfflineImageURL,
					ViewCount:       user.ViewCount,
					Email:           user.Email,
				}
				userCacheByID[user.ID] = data
				userCacheByUsername[user.Login] = data
			}
		}
	}

	result := make(map[string]UserData)

	for _, id := range userIDs {
		if _, ok := userCacheByID[id]; !ok {
			log.Errorf("Could not find userId, channel might be banned: %s", id)
			continue
		}
		result[id] = *userCacheByID[id]
	}

	return result, nil
}

// GetUsersByUsernames fetches userdata from helix
func (c *Client) GetUsersByUsernames(usernames []string) (map[string]UserData, error) {
	var filteredUsernames []string

	for _, username := range usernames {
		if _, ok := userCacheByUsername[strings.ToLower(username)]; !ok {
			filteredUsernames = append(filteredUsernames, strings.ToLower(username))
		}
	}

	if len(filteredUsernames) > 0 {
		resp, err := c.client.GetUsers(&helixClient.UsersParams{
			Logins: filteredUsernames,
		})
		if err != nil {
			return map[string]UserData{}, err
		}

		log.Infof("[helix] %d GetUsersByUsernames %v", resp.StatusCode, filteredUsernames)
		if resp.StatusCode > http.StatusMultipleChoices {
			return map[string]UserData{}, fmt.Errorf("bad helix response: %v", resp.ErrorMessage)
		}

		for _, user := range resp.Data.Users {
			data := &UserData{
				ID:              user.ID,
				Login:           user.Login,
				DisplayName:     user.Login,
				Type:            user.Type,
				BroadcasterType: user.BroadcasterType,
				Description:     user.Description,
				ProfileImageURL: user.ProfileImageURL,
				OfflineImageURL: user.OfflineImageURL,
				ViewCount:       user.ViewCount,
				Email:           user.Email,
			}
			userCacheByID[user.ID] = data
			userCacheByUsername[user.Login] = data
		}
	}

	result := make(map[string]UserData)

	for _, username := range usernames {
		result[strings.ToLower(username)] = *userCacheByUsername[strings.ToLower(username)]
	}

	return result, nil
}
<commit_msg>pass secret<commit_after>package helix

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

	helixClient "github.com/nicklaw5/helix"
	log "github.com/sirupsen/logrus"
)

// Client wrapper for helix
type Client struct {
	clientID     string
	clientSecret string
	client       *helixClient.Client
	httpClient   *http.Client
}

var (
	userCacheByID       map[string]*UserData
	userCacheByUsername map[string]*UserData
)

func init() {
	userCacheByID = map[string]*UserData{}
	userCacheByUsername = map[string]*UserData{}
}

// NewClient Create helix client
func NewClient(clientID, clientSecret string) Client {
	client, err := helixClient.NewClient(&helixClient.Options{
		ClientID:     clientID,
		ClientSecret: clientSecret,
	})
	if err != nil {
		panic(err)
	}

	resp, err := client.RequestAppAccessToken([]string{})
	if err != nil {
		panic(err)
	}
	log.Infof("Requested access token, response: %d, expires in: %d", resp.StatusCode, resp.Data.ExpiresIn)
	client.SetAppAccessToken(resp.Data.AccessToken)

	return Client{
		clientID:     clientID,
		clientSecret: clientSecret,
		client:       client,
		httpClient:   &http.Client{},
	}
}

// StartRefreshTokenRoutine refresh our token
func (c *Client) StartRefreshTokenRoutine() {
	ticker := time.NewTicker(24 * time.Hour)

	for range ticker.C {
		resp, err := c.client.RequestAppAccessToken([]string{})
		if err != nil {
			log.Error(err)
			continue
		}
		log.Infof("Requested access token from routine, response: %d, expires in: %d", resp.StatusCode, resp.Data.ExpiresIn)

		c.client.SetAppAccessToken(resp.Data.AccessToken)
	}
}

type userResponse struct {
	Data []UserData `json:"data"`
}

// UserData exported data from twitch
type UserData struct {
	ID              string `json:"id"`
	Login           string `json:"login"`
	DisplayName     string `json:"display_name"`
	Type            string `json:"type"`
	BroadcasterType string `json:"broadcaster_type"`
	Description     string `json:"description"`
	ProfileImageURL string `json:"profile_image_url"`
	OfflineImageURL string `json:"offline_image_url"`
	ViewCount       int    `json:"view_count"`
	Email           string `json:"email"`
}

// GetUsersByUserIds receive userData for given ids
func (c *Client) GetUsersByUserIds(userIDs []string) (map[string]UserData, error) {
	var filteredUserIDs []string

	for _, id := range userIDs {
		if _, ok := userCacheByID[id]; !ok {
			filteredUserIDs = append(filteredUserIDs, id)
		}
	}

	if len(filteredUserIDs) > 0 {
		var chunks [][]string
		for i := 0; i < len(filteredUserIDs); i += 100 {
			end := i + 100

			if end > len(filteredUserIDs) {
				end = len(filteredUserIDs)
			}

			chunks = append(chunks, filteredUserIDs[i:end])
		}

		for _, chunk := range chunks {
			resp, err := c.client.GetUsers(&helixClient.UsersParams{
				IDs: chunk,
			})
			if err != nil {
				return make(map[string]UserData), err
			}
			log.Infof("[helix] %d GetUsersByUserIds %v", resp.StatusCode, chunk)
			if resp.StatusCode > http.StatusMultipleChoices {
				return make(map[string]UserData), fmt.Errorf("bad helix response: %v", resp.ErrorMessage)
			}

			for _, user := range resp.Data.Users {
				data := &UserData{
					ID:              user.ID,
					Login:           user.Login,
					DisplayName:     user.Login,
					Type:            user.Type,
					BroadcasterType: user.BroadcasterType,
					Description:     user.Description,
					ProfileImageURL: user.ProfileImageURL,
					OfflineImageURL: user.OfflineImageURL,
					ViewCount:       user.ViewCount,
					Email:           user.Email,
				}
				userCacheByID[user.ID] = data
				userCacheByUsername[user.Login] = data
			}
		}
	}

	result := make(map[string]UserData)

	for _, id := range userIDs {
		if _, ok := userCacheByID[id]; !ok {
			log.Errorf("Could not find userId, channel might be banned: %s", id)
			continue
		}
		result[id] = *userCacheByID[id]
	}

	return result, nil
}

// GetUsersByUsernames fetches userdata from helix
func (c *Client) GetUsersByUsernames(usernames []string) (map[string]UserData, error) {
	var filteredUsernames []string

	for _, username := range usernames {
		if _, ok := userCacheByUsername[strings.ToLower(username)]; !ok {
			filteredUsernames = append(filteredUsernames, strings.ToLower(username))
		}
	}

	if len(filteredUsernames) > 0 {
		resp, err := c.client.GetUsers(&helixClient.UsersParams{
			Logins: filteredUsernames,
		})
		if err != nil {
			return map[string]UserData{}, err
		}

		log.Infof("[helix] %d GetUsersByUsernames %v", resp.StatusCode, filteredUsernames)
		if resp.StatusCode > http.StatusMultipleChoices {
			return map[string]UserData{}, fmt.Errorf("bad helix response: %v", resp.ErrorMessage)
		}

		for _, user := range resp.Data.Users {
			data := &UserData{
				ID:              user.ID,
				Login:           user.Login,
				DisplayName:     user.Login,
				Type:            user.Type,
				BroadcasterType: user.BroadcasterType,
				Description:     user.Description,
				ProfileImageURL: user.ProfileImageURL,
				OfflineImageURL: user.OfflineImageURL,
				ViewCount:       user.ViewCount,
				Email:           user.Email,
			}
			userCacheByID[user.ID] = data
			userCacheByUsername[user.Login] = data
		}
	}

	result := make(map[string]UserData)

	for _, username := range usernames {
		result[strings.ToLower(username)] = *userCacheByUsername[strings.ToLower(username)]
	}

	return result, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, huangjunwei <huangjunwei@youmi.net>. All rights reserved.

package blog4go

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

type T struct {
	A int
	B string
}

func TestFileWriterBasicOperation(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	// test file writer hook
	hook := new(MyHook)
	hook.cnt = 0

	blog.SetHook(hook)
	blog.SetHookLevel(INFO)

	blog.Debug("something")
	// wait for hook called
	time.Sleep(1 * time.Millisecond)
	if 0 != hook.cnt {
		t.Error("hook called not valid")
	}

	if DEBUG == hook.level || "something" == hook.message {
		t.Errorf("hook parameters wrong. level: %s, message: %s", hook.level.String(), hook.message)
	}

	blog.Info("yes")
	// wait for hook called
	time.Sleep(1 * time.Millisecond)
	if 1 != hook.cnt {
		t.Error("hook not called")
	}

	if INFO != hook.level || "yes" != hook.message {
		t.Errorf("hook parameters wrong. level: %d, message: %s", hook.level, hook.message)
	}

	// test basic operations
	blog.SetColored(true)
	blog.SetTimeRotated(true)
	blog.SetLevel(INFO)
	blog.SetRetentions(7)
	blog.SetRotateLines(100000)
	blog.SetRotateSize(ByteSize(1024 * 1024 * 500))

	blog.Debug("Debug")
	blog.Debugf("%s", "Debug")
	blog.Trace("Trace")
	blog.Tracef("%s", "Trace")
	blog.Info("Info")
	blog.Infof("%s", "Info")
	blog.Warn("Warn")
	blog.Warnf("%s", "Warn")
	blog.Error("Error")
	blog.Errorf("%s", "Error")
	blog.Critical("Critical")
	blog.Criticalf("%s", "Critical")
}

func TestSingleFileWriter(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	// duplicate initialization test
	err = NewFileWriter("/tmp", false)
	if ErrAlreadyInit != err {
		t.Error("duplicate init check fail")
	}

	// should be closed
	Close()
	if nil != blog {
		t.Error("blog should be closed.")
	}
}

func TestFileWriterAsConfigFile(t *testing.T) {
	err := NewWriterFromConfigAsFile("examples/writer_from_configfile/config.example.xml")
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

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

	blog.Debug("Debug")
	blog.Trace("Trace")
	blog.Info("Info")
	blog.Warn("Warn")
	blog.Error("Error")
	blog.Critical("Critical")
}

// test if log lose in multi goroutine mode
func TestFileWriterMultiGoroutine(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()
	temp := T{123, "test"}

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	var wg sync.WaitGroup
	var beginWg sync.WaitGroup

	f := func() {
		defer wg.Done()
		beginWg.Wait()
		for i := 0; i < 100; i++ {
			blog.Infof("haha %s. en\\en, always %d and %f, %t, %+v", "eddie", 18, 3.1415, true, temp)
		}
	}

	for i := 0; i < 100; i++ {
		wg.Add(1)
		beginWg.Add(1)
	}

	// write 100 * 100 lines to /tmp/info.log
	for i := 0; i < 100; i++ {
		go f()
		beginWg.Done()
	}

	wg.Wait()
	Flush()

	out, err := exec.Command("/bin/sh", "-c", "/usr/bin/wc -l /tmp/info.log").Output()
	if nil != err {
		t.Errorf("count file lines failed. err: %s", err.Error())
	}

	arr := strings.Split(string(out), " ")
	intStr := arr[len(arr)-2]
	lines, err := strconv.Atoi(intStr)
	if nil != err {
		t.Errorf("line string convert to int failed. err: %s", err.Error())
	}

	if 100*100 != lines {
		t.Errorf("it loses %d lines.", 100*100-lines)
	}

	// check log message line by line
	file, err := os.Open("/tmp/info.log")
	if err != nil {
		t.Error(err.Error())

	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	line := 0
	for scanner.Scan() {
		line++
		lineStr := scanner.Text()
		arrs := strings.Split(lineStr, "[INFO] ")
		if len(arrs) != 2 {
			t.Errorf("line %d detect inconsistent line. not formatted. lineStr: %s", line, lineStr)
		}

		if "haha eddie. en\\en, always 18 and 3.141500, true, {A:123 B:test}" != arrs[1] {
			t.Errorf("line %d detect inconsistent line. message not correct. lineStr: %s", line, lineStr)
		}
	}

	if err := scanner.Err(); err != nil {
		t.Error(err.Error())
	}
}

func TestFileWriterSizeBaseLogrotate(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}
	blog.SetRotateSize(2)

	blog.Info("1")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); nil == err {
		t.Errorf("size base logrotate failed, log should not exist. err: %s", err.Error())
	}

	blog.Info("2")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("3")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); os.IsNotExist(err) {
		t.Errorf("size base logrotate failed., err: %s", err.Error())
	}
}

// TODO how to test time base logratate ?
func TestFileWriterTimeBaseLogrotate(t *testing.T) {
	err := NewFileWriter("/tmp", true)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	SetRetentions(1)

	// check if formatted file name exist
	if _, err = os.Stat(fmt.Sprintf("/tmp/trace.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/debug.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/info.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/warn.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/error.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/critical.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
}

func TestFileWriterLinesBaseLogrotate(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	blog.SetRotateLines(2)
	blog.Info("some")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); nil == err {
		t.Errorf("line base logrotate failed, log should not exist. err: %s", err.Error())
	}

	blog.Info("some")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("some")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); os.IsNotExist(err) {
		t.Errorf("line base logrotate failed. err: %s", err.Error())
	}
}

func TestFileWriterLogrorateRetentionCount(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	blog.SetRotateLines(2)
	blog.SetRetentions(1)

	blog.Info("1")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); nil == err {
		t.Errorf("logrotate retention failed, log should not exist. err: %s", err.Error())
	}

	blog.Info("2")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("3")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("4")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("5")
	Flush()
	time.Sleep(1 * time.Millisecond)
	if _, err = os.Stat("/tmp/info.log.2"); nil == err {
		t.Errorf("logrotate retention failed. err: %s", err.Error())
	}
}

func TestFileWriterSinglton(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	err = NewFileWriter("/tmp", false)
	if ErrAlreadyInit != err {
		t.Errorf("file writer singlton failed. err: %s", err.Error())
	}
}

func BenchmarkFileWriters(b *testing.B) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			b.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		b.Error(err.Error())
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		blog.Debugf("haha %s. en\\en, always %d and %f", "eddie", 18, 3.1415)
	}
	Flush()
}
<commit_msg>fix test<commit_after>// Copyright (c) 2015, huangjunwei <huangjunwei@youmi.net>. All rights reserved.

package blog4go

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

type T struct {
	A int
	B string
}

func TestFileWriterBasicOperation(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	// test file writer hook
	hook := new(MyHook)
	hook.cnt = 0

	blog.SetHook(hook)
	blog.SetHookLevel(INFO)

	blog.Debug("something")
	// wait for hook called
	time.Sleep(1 * time.Millisecond)
	if 0 != hook.cnt {
		t.Error("hook called not valid")
	}

	if DEBUG == hook.level || "something" == hook.message {
		t.Errorf("hook parameters wrong. level: %s, message: %s", hook.level.String(), hook.message)
	}

	blog.Info("yes")
	// wait for hook called
	time.Sleep(1 * time.Millisecond)
	if 1 != hook.cnt {
		t.Error("hook not called")
	}

	if INFO != hook.level || "yes" != hook.message {
		t.Errorf("hook parameters wrong. level: %d, message: %s", hook.level, hook.message)
	}

	// test basic operations
	blog.SetColored(true)
	blog.SetTimeRotated(true)
	blog.SetLevel(INFO)
	blog.SetRetentions(7)
	blog.SetRotateLines(100000)
	blog.SetRotateSize(ByteSize(1024 * 1024 * 500))

	blog.Debug("Debug")
	blog.Debugf("%s", "Debug")
	blog.Trace("Trace")
	blog.Tracef("%s", "Trace")
	blog.Info("Info")
	blog.Infof("%s", "Info")
	blog.Warn("Warn")
	blog.Warnf("%s", "Warn")
	blog.Error("Error")
	blog.Errorf("%s", "Error")
	blog.Critical("Critical")
	blog.Criticalf("%s", "Critical")
}

func TestSingleFileWriter(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	// duplicate initialization test
	err = NewFileWriter("/tmp", false)
	if ErrAlreadyInit != err {
		t.Error("duplicate init check fail")
	}

	// should be closed
	Close()
	if nil != blog {
		t.Error("blog should be closed.")
	}
}

func TestFileWriterAsConfigFile(t *testing.T) {
	err := NewWriterFromConfigAsFile("examples/writer_from_configfile/config.example.xml")
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

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

	blog.Debug("Debug")
	blog.Trace("Trace")
	blog.Info("Info")
	blog.Warn("Warn")
	blog.Error("Error")
	blog.Critical("Critical")
}

// test if log lose in multi goroutine mode
func TestFileWriterMultiGoroutine(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()
	temp := T{123, "test"}

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	var wg sync.WaitGroup
	var beginWg sync.WaitGroup

	f := func() {
		defer wg.Done()
		beginWg.Wait()
		for i := 0; i < 100; i++ {
			blog.Infof("haha %s. en\\en, always %d and %f, %t, %+v", "eddie", 18, 3.1415, true, temp)
		}
	}

	for i := 0; i < 100; i++ {
		wg.Add(1)
		beginWg.Add(1)
	}

	// write 100 * 100 lines to /tmp/info.log
	for i := 0; i < 100; i++ {
		go f()
		beginWg.Done()
	}

	wg.Wait()
	Flush()

	out, err := exec.Command("/bin/sh", "-c", "/usr/bin/wc -l /tmp/info.log").Output()
	if nil != err {
		t.Errorf("count file lines failed. err: %s", err.Error())
	}

	arr := strings.Split(string(out), " ")
	intStr := arr[len(arr)-2]
	lines, err := strconv.Atoi(intStr)
	if nil != err {
		t.Errorf("line string convert to int failed. err: %s", err.Error())
	}

	if 100*100 != lines {
		t.Errorf("it loses %d lines.", 100*100-lines)
	}

	// check log message line by line
	file, err := os.Open("/tmp/info.log")
	if err != nil {
		t.Error(err.Error())

	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	line := 0
	for scanner.Scan() {
		line++
		lineStr := scanner.Text()
		arrs := strings.Split(lineStr, "[INFO] ")
		if len(arrs) != 2 {
			t.Errorf("line %d detect inconsistent line. not formatted. lineStr: %s", line, lineStr)
		}

		if "haha eddie. en\\en, always 18 and 3.141500, true, {A:123 B:test}" != arrs[1] {
			t.Errorf("line %d detect inconsistent line. message not correct. lineStr: %s", line, lineStr)
		}
	}

	if err := scanner.Err(); err != nil {
		t.Error(err.Error())
	}
}

func TestFileWriterSizeBaseLogrotate(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		//_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		//if nil != err {
		//t.Errorf("clean files failed. err: %s", err.Error())
		//}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}
	blog.SetRotateSize(60)

	blog.Info("1")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); nil == err {
		t.Error("size base logrotate failed, log should not exist.")
	}

	blog.Info("2")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("3")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); os.IsNotExist(err) {
		t.Errorf("size base logrotate failed. err: %s", err.Error())
	}
}

// TODO how to test time base logratate ?
func TestFileWriterTimeBaseLogrotate(t *testing.T) {
	err := NewFileWriter("/tmp", true)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	SetRetentions(1)

	// check if formatted file name exist
	if _, err = os.Stat(fmt.Sprintf("/tmp/trace.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/debug.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/info.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/warn.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/error.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
	if _, err = os.Stat(fmt.Sprintf("/tmp/critical.log.%s", timeCache.date)); os.IsNotExist(err) {
		t.Error("time base logrotate formatted file name incorrect.")
	}
}

func TestFileWriterLinesBaseLogrotate(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	blog.SetRotateLines(2)
	blog.Info("some")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); nil == err {
		t.Error("line base logrotate failed, log should not exist.")
	}

	blog.Info("some")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("some")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); os.IsNotExist(err) {
		t.Errorf("line base logrotate failed. err: %s", err.Error())
	}
}

func TestFileWriterLogrorateRetentionCount(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	blog.SetRotateLines(2)
	blog.SetRetentions(1)

	blog.Info("1")
	Flush()
	time.Sleep(1 * time.Millisecond)

	if _, err = os.Stat("/tmp/info.log.1"); nil == err {
		t.Error("logrotate retention failed, log should not exist.")
	}

	blog.Info("2")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("3")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("4")
	Flush()
	time.Sleep(1 * time.Millisecond)

	blog.Info("5")
	Flush()
	time.Sleep(1 * time.Millisecond)
	if _, err = os.Stat("/tmp/info.log.2"); nil == err {
		t.Error("logrotate retention failed.")
	}
}

func TestFileWriterSinglton(t *testing.T) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			t.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		t.Errorf("initialize file writer faied. err: %s", err.Error())
	}

	err = NewFileWriter("/tmp", false)
	if ErrAlreadyInit != err {
		t.Errorf("file writer singlton failed. err: %s", err.Error())
	}
}

func BenchmarkFileWriters(b *testing.B) {
	err := NewFileWriter("/tmp", false)
	defer func() {
		Close()

		// clean logs
		_, err = exec.Command("/bin/sh", "-c", "/bin/rm /tmp/*.log*").Output()
		if nil != err {
			b.Errorf("clean files failed. err: %s", err.Error())
		}
	}()

	if nil != err {
		b.Error(err.Error())
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		blog.Debugf("haha %s. en\\en, always %d and %f", "eddie", 18, 3.1415)
	}
	Flush()
}
<|endoftext|>


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

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
	"github.com/lxc/lxd"
)

// Client is a high-level wrapper around the LXD API client.
type Client struct {
	raw       rawClientWrapper
	namespace string
}

// Connect opens an API connection to LXD and returns a high-level
// Client wrapper around that connection.
func Connect(cfg Config) (*Client, error) {
	if err := cfg.Apply(); err != nil {
		return nil, errors.Trace(err)
	}

	raw, err := newRawClient(cfg.Remote)
	if err != nil {
		return nil, errors.Trace(err)
	}

	conn := &Client{
		raw:       raw,
		namespace: cfg.Namespace,
	}
	return conn, nil
}

func newRawClient(remote Remote) (*lxd.Client, error) {
	cfg, err := lxd.LoadConfig()
	if err != nil {
		return nil, errors.Trace(err)
	}

	client, err := lxd.NewClient(cfg, remote.ID())
	if err != nil {
		return nil, errors.Trace(err)
	}

	return client, nil
}
<commit_msg>Add "remote" field to lxdclient.Config.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
	"github.com/lxc/lxd"
)

// Client is a high-level wrapper around the LXD API client.
type Client struct {
	raw       rawClientWrapper
	namespace string
	remote    string
}

// Connect opens an API connection to LXD and returns a high-level
// Client wrapper around that connection.
func Connect(cfg Config) (*Client, error) {
	if err := cfg.Apply(); err != nil {
		return nil, errors.Trace(err)
	}
	remote := cfg.Remote.ID()

	raw, err := newRawClient(remote)
	if err != nil {
		return nil, errors.Trace(err)
	}

	conn := &Client{
		raw:       raw,
		namespace: cfg.Namespace,
		remote:    remote,
	}
	return conn, nil
}

func newRawClient(remote string) (*lxd.Client, error) {
	logger.Debugf("loading LXD client config from %q", lxd.ConfigDir)

	cfg, err := lxd.LoadConfig()
	if err != nil {
		return nil, errors.Trace(err)
	}

	logger.Debugf("using LXD remote %q", remote)
	client, err := lxd.NewClient(cfg, remote)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return client, nil
}
<|endoftext|>


<commit_before>package kk

import (
	"github.com/kkserver/kk-direct/direct"
	"github.com/kkserver/kk-lib/kk/app"
	"github.com/kkserver/kk-lib/kk/app/client"
	"github.com/kkserver/kk-lib/kk/dynamic"
	"log"
	"time"
)

var AppKeys = []string{"app"}
var ResultKeys = []string{"result"}

type Direct struct {
	direct.Direct
}

func (D *Direct) Exec(ctx direct.IContext) error {

	options := D.Options()

	v := ctx.Get(AppKeys)

	if v != nil {

		a, ok := v.(app.IApp)

		if ok {

			task := client.RequestTask{}

			task.Name = options.Name()
			task.Timeout = time.Duration(dynamic.IntValue(dynamic.Get(options, "timeout"), 1)) * time.Second

			v, ok = options["options"]

			if ok {
				task.Request = direct.ReflectValue(D.App(), ctx, v)
			} else {
				task.Request = map[interface{}]interface{}{}
			}

			log.Println("kk", task)

			err := app.Handle(a, &task)

			log.Println("kk", task, err)

			if err != nil {
				return D.Fail(ctx, err)
			}

			errno := dynamic.IntValue(dynamic.Get(task.Result, "errno"), 0)
			errmsg := dynamic.StringValue(dynamic.Get(task.Result, "errmsg"), "")

			if errno != 0 {
				return D.Fail(ctx, direct.NewError(errno, errmsg))
			}

			ctx.Set(ResultKeys, task.Result)

			return D.Done(ctx, "done")
		}
	}

	return D.Fail(ctx, direct.NewError(direct.ERROR_UNKNOWN, "Not Found kk app"))
}
<commit_msg>fix bug<commit_after>package kk

import (
	"github.com/kkserver/kk-direct/direct"
	"github.com/kkserver/kk-lib/kk/app"
	"github.com/kkserver/kk-lib/kk/app/client"
	"github.com/kkserver/kk-lib/kk/dynamic"
	"log"
	"time"
)

var AppKeys = []string{"app"}
var ResultKeys = []string{"result"}

type Direct struct {
	direct.Direct
}

func (D *Direct) Exec(ctx direct.IContext) error {

	options := D.Options()

	v := ctx.Get(AppKeys)

	if v != nil {

		a, ok := v.(app.IApp)

		if ok {

			task := client.RequestTask{}

			task.Name = options.Name()
			task.Timeout = time.Duration(dynamic.IntValue(dynamic.Get(options, "timeout"), 1)) * time.Second

			v, ok = options["options"]

			if ok {
				task.Request = direct.ReflectValue(D.App(), ctx, v)
			} else {
				task.Request = map[interface{}]interface{}{}
			}

			log.Println("kk", task)

			err := app.Handle(a, &task)

			log.Println("kk", task, err)

			if err != nil {
				return D.Fail(ctx, err)
			}

			errno := int(dynamic.IntValue(dynamic.Get(task.Result, "errno"), 0))
			errmsg := dynamic.StringValue(dynamic.Get(task.Result, "errmsg"), "")

			if errno != 0 {
				return D.Fail(ctx, direct.NewError(errno, errmsg))
			}

			ctx.Set(ResultKeys, task.Result)

			return D.Done(ctx, "done")
		}
	}

	return D.Fail(ctx, direct.NewError(direct.ERROR_UNKNOWN, "Not Found kk app"))
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/EaseApp/web-backend/src/app/controllers"
	"github.com/EaseApp/web-backend/src/app/models"
	"github.com/EaseApp/web-backend/src/db"
	"github.com/gorilla/mux"
)

// CreateRoutingMux sets up the routing for the server.
func CreateRoutingMux(client *db.Client) *mux.Router {

	// Set up the queriers and controllers.
	userQuerier := models.NewUserQuerier(client.Session)

	controllers.InitUserController(userQuerier)

	router := mux.Router()

	//	router.HandleFunc().Methods("POST")
	return router
}
<commit_msg>User endpoint routes.<commit_after>package server

import (
	"github.com/EaseApp/web-backend/src/app/controllers"
	"github.com/EaseApp/web-backend/src/app/models"
	"github.com/EaseApp/web-backend/src/db"
	"github.com/gorilla/mux"
)

// CreateRoutingMux sets up the routing for the server.
func CreateRoutingMux(client *db.Client) *mux.Router {

	// Set up the queriers and controllers.
	userQuerier := models.NewUserQuerier(client.Session)

	controllers.InitUserController(userQuerier)

	router := mux.NewRouter()

	// User routes.
	router.HandleFunc("/users/sign_up", controllers.SignUpHandler).Methods("POST")
	router.HandleFunc("/users/sign_in", controllers.SignInHandler).Methods("POST")

	return router
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"

	"github.com/EaseApp/web-backend/src/app/controllers/applicationcontroller"
	"github.com/EaseApp/web-backend/src/app/controllers/helpers"
	"github.com/EaseApp/web-backend/src/app/controllers/usercontroller"
	"github.com/EaseApp/web-backend/src/app/models"
	"github.com/EaseApp/web-backend/src/db"
	"github.com/gorilla/mux"
)

// EaseServer serves while allowing cross origin access.
type EaseServer struct {
	r *mux.Router
}

// NewEaseServer creates a new handler for Ease.
func NewEaseServer(client *db.Client) *EaseServer {
	return &EaseServer{r: createRoutingMux(client)}
}

// ServeHTTP serves requests from the EaseServer's mux while allowing
// cross origin access.
func (s *EaseServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if origin := req.Header.Get("Origin"); origin != "" {
		rw.Header().Set("Access-Control-Allow-Origin", origin)
		rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		rw.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
	}
	// Stop here if its Preflighted OPTIONS request
	if req.Method == "OPTIONS" {
		return
	}

	// Lets Gorilla work
	s.r.ServeHTTP(rw, req)
}

// createRoutingMux sets up the routing for the server.
func createRoutingMux(client *db.Client) *mux.Router {

	// Set up the queriers and controllers.
	querier := models.NewModelQuerier(client.Session)

	usercontroller.Init(querier)
	applicationcontroller.Init(querier)
	helpers.Init(querier)

	router := mux.NewRouter()

	router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})

	// User routes.
	router.HandleFunc("/users/sign_up", usercontroller.SignUpHandler).Methods("POST")
	router.HandleFunc("/users/sign_in", usercontroller.SignInHandler).Methods("POST")
	router.HandleFunc("/users/applications/{application}",
		helpers.RequireAPIToken(applicationcontroller.CreateApplicationHandler)).Methods("POST")
	router.HandleFunc("/users/applications",
		helpers.RequireAPIToken(applicationcontroller.ListApplicationsHandler)).Methods("GET")
	router.HandleFunc("/users/applications/{application}",
		helpers.RequireAPIToken(applicationcontroller.DeleteApplicationHandler)).Methods("DELETE")

	return router
}
<commit_msg>Routes for crud endpoints.<commit_after>package server

import (
	"fmt"
	"net/http"

	"github.com/EaseApp/web-backend/src/app/controllers/applicationcontroller"
	"github.com/EaseApp/web-backend/src/app/controllers/helpers"
	"github.com/EaseApp/web-backend/src/app/controllers/usercontroller"
	"github.com/EaseApp/web-backend/src/app/models"
	"github.com/EaseApp/web-backend/src/db"
	"github.com/gorilla/mux"
)

// EaseServer serves while allowing cross origin access.
type EaseServer struct {
	r *mux.Router
}

// NewEaseServer creates a new handler for Ease.
func NewEaseServer(client *db.Client) *EaseServer {
	return &EaseServer{r: createRoutingMux(client)}
}

// ServeHTTP serves requests from the EaseServer's mux while allowing
// cross origin access.
func (s *EaseServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if origin := req.Header.Get("Origin"); origin != "" {
		rw.Header().Set("Access-Control-Allow-Origin", origin)
		rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		rw.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
	}
	// Stop here if its Preflighted OPTIONS request
	if req.Method == "OPTIONS" {
		return
	}

	// Lets Gorilla work
	s.r.ServeHTTP(rw, req)
}

// createRoutingMux sets up the routing for the server.
func createRoutingMux(client *db.Client) *mux.Router {

	// Set up the queriers and controllers.
	querier := models.NewModelQuerier(client.Session)

	usercontroller.Init(querier)
	applicationcontroller.Init(querier)
	helpers.Init(querier)

	router := mux.NewRouter()

	router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})

	// User routes.
	router.HandleFunc("/users/sign_up", usercontroller.SignUpHandler).Methods("POST")
	router.HandleFunc("/users/sign_in", usercontroller.SignInHandler).Methods("POST")
	router.HandleFunc("/users/applications/{application}",
		helpers.RequireAPIToken(applicationcontroller.CreateApplicationHandler)).Methods("POST")
	router.HandleFunc("/users/applications",
		helpers.RequireAPIToken(applicationcontroller.ListApplicationsHandler)).Methods("GET")
	router.HandleFunc("/users/applications/{application}",
		helpers.RequireAPIToken(applicationcontroller.DeleteApplicationHandler)).Methods("DELETE")

	// Application data routes.
	router.HandleFunc("/data/{username}/{app_name}",
		helpers.RequireAppToken(applicationcontroller.ReadApplicationDataHandler)).Methods("GET")
	router.HandleFunc("/data/{username}/{app_name}",
		helpers.RequireAppToken(applicationcontroller.SaveApplicationDataHandler)).Methods("POST")
	router.HandleFunc("/data/{username}/{app_name}",
		helpers.RequireAppToken(applicationcontroller.DeleteApplicationDataHandler)).Methods("DELETE")

	return router
}
<|endoftext|>


<commit_before>/*

The html transform package implements a html css selector and transformer.

An html doc can be inspected and queried using css selectors as well as
transformed.

	doc := NewDoc(str)
	t := NewTransform(doc)
	t.Apply(CopyAnd(myModifiers...), "li.menuitem")
	t.Apply(Replace(Text("my new text"), "a")
	newDoc := t.Doc()
*/
package transform

// TODO(jwall): Documentation...
import (
	. "code.google.com/p/go-html-transform/h5"
	"log"
)

// The TransformFunc type is the type of a Node transformation function.
type TransformFunc func(*Node)

// Transformer encapsulates a document under transformation.
type Transformer struct {
	doc *Node
}

// Constructor for a Transformer. It makes a copy of the document
// and transforms that instead of the original.
func NewTransform(d *Node) *Transformer {
	return &Transformer{doc: d.Clone()}
}

// The Doc method returns the document under transformation.
func (t *Transformer) Doc() *Node {
	return t.doc
}

func (t *Transformer) String() string {
	return t.doc.String()
}

// TODO(jwall): TransformApplication type that we can process the doc in one
// pass.
// The Apply method applies a TransformFunc to the nodes returned from
// the Selector query
func (t *Transformer) Apply(f TransformFunc, sel ...string) *Transformer {
	// TODO come up with a way to walk tree once?
	sq := NewSelectorQuery(sel...)
	nodes := sq.Apply(t.doc)
	for _, n := range nodes {
		f(n)
	}
	return t
}

// Compose a set of TransformFuncs into a single TransformFunc
func Compose(fs ...TransformFunc) TransformFunc {
	return func(n *Node) {
		for _, f := range fs {
			f(n)
		}
	}
}

// AppendChildren creates a TransformFunc that appends the Children passed in.
func AppendChildren(cs ...*Node) TransformFunc {
	return func(n *Node) {
		sz := len(n.Children)
		newChild := make([]*Node, sz+len(cs))
		copy(newChild, n.Children)
		copy(newChild[sz:], cs)
		n.Children = newChild
	}
}

// PrependChildren creates a TransformFunc that prepends the Children passed in.
func PrependChildren(cs ...*Node) TransformFunc {
	return func(n *Node) {
		sz := len(n.Children)
		sz2 := len(cs)
		newChild := make([]*Node, sz+len(cs))
		copy(newChild[sz2:], n.Children)
		copy(newChild[0:sz2], cs)
		n.Children = newChild
	}
}

// RemoveChildren creates a TransformFunc that removes the Children of the node
// it operates on.
func RemoveChildren() TransformFunc {
	return func(n *Node) {
		n.Children = make([]*Node, 0)
	}
}

// ReplaceChildren creates a TransformFunc that replaces the Children of the
// node it operates on with the Children passed in.
func ReplaceChildren(ns ...*Node) TransformFunc {
	return func(n *Node) {
		n.Children = ns
	}
}

func Replace(ns ...*Node) TransformFunc {
	return func(n *Node) {
		p := n.Parent
		switch p {
		case nil:
			n.Children = ns
		default:
			for i, c := range p.Children {
				if c == n {
					n := i - 1
					if n < 0 {
						n = 0
					}
					var newChild []*Node
					pre := p.Children[:n]
					post := p.Children[i+1:]
					newChild = append(pre, ns...)
					p.Children = append(newChild, post...)
				}
			}
		}
		// TODO(jwall): splice the new nodes into the spot the current node is
	}
}

// ModifyAttrb creates a TransformFunc that modifies the attributes
// of the node it operates on.
func ModifyAttrib(key string, val string) TransformFunc {
	return func(n *Node) {
		found := false
		for i, attr := range n.Attr {
			if attr.Name == key {
				n.Attr[i].Value = val
				found = true
			}
		}
		if !found {
			newAttr := make([]*Attribute, len(n.Attr)+1)
			newAttr[len(n.Attr)] = &Attribute{Name: key, Value: val}
			n.Attr = newAttr
		}
	}
}

func TransformAttrib(key string, f func(string) string) TransformFunc {
	return func(n *Node) {
		for i, attr := range n.Attr {
			if attr.Name == key {
				n.Attr[i].Value = f(n.Attr[i].Value)
			}
		}
	}
}

func DoAll(fs ...TransformFunc) TransformFunc {
	return func(n *Node) {
		for _, f := range fs {
			f(n)
		}
	}
}

// ForEach takes a function and a list of Nodes and performs that
// function for each node in the list.
// The function should be of a type either func(...*Node) TransformFunc
// or func(*Node) TransformFunc. Any other type will panic.
// Returns a TransformFunc.
func ForEach(f interface{}, ns ...*Node) TransformFunc {
	switch t := f.(type) {
	case func(...*Node) TransformFunc:
		return func(n *Node) {
			for _, n2 := range ns {
				f1 := f.(func(...*Node) TransformFunc)
				f2 := f1(n2)
				f2(n)
			}
		}
	case func(*Node) TransformFunc:
		return func(n *Node) {
			for _, n2 := range ns {
				f1 := f.(func(*Node) TransformFunc)
				f2 := f1(n2)
				f2(n)
			}
		}
	default:
		log.Panicf("Wrong function type passed to ForEach %s", t)
	}
	return nil
}

// CopyAnd will construct a TransformFunc that will
// make a copy of the node for each passed in TransformFunc
// And replace the passed in node with the resulting transformed
// Nodes.
// Returns a TransformFunc
func CopyAnd(fns ...TransformFunc) TransformFunc {
	return func(n *Node) {
		newNodes := make([]*Node, len(fns))
		for i, fn := range fns {
			node := n.Clone()
			fn(node)
			newNodes[i] = node
		}
		replaceFn := Replace(newNodes...)
		replaceFn(n)
	}
}
// Copyright 2010 Jeremy Wall (jeremy@marzhillstudios.com)
// Use of this source code is governed by the Artistic License 2.0.
// That License is included in the LICENSE file.
<commit_msg>remove todo.<commit_after>/*

The html transform package implements a html css selector and transformer.

An html doc can be inspected and queried using css selectors as well as
transformed.

	doc := NewDoc(str)
	t := NewTransform(doc)
	t.Apply(CopyAnd(myModifiers...), "li.menuitem")
	t.Apply(Replace(Text("my new text"), "a")
	newDoc := t.Doc()
*/
package transform

// TODO(jwall): Documentation...
import (
	. "code.google.com/p/go-html-transform/h5"
	"log"
)

// The TransformFunc type is the type of a Node transformation function.
type TransformFunc func(*Node)

// Transformer encapsulates a document under transformation.
type Transformer struct {
	doc *Node
}

// Constructor for a Transformer. It makes a copy of the document
// and transforms that instead of the original.
func NewTransform(d *Node) *Transformer {
	return &Transformer{doc: d.Clone()}
}

// The Doc method returns the document under transformation.
func (t *Transformer) Doc() *Node {
	return t.doc
}

func (t *Transformer) String() string {
	return t.doc.String()
}

// TODO(jwall): TransformApplication type that we can process the doc in one
// pass.
// The Apply method applies a TransformFunc to the nodes returned from
// the Selector query
func (t *Transformer) Apply(f TransformFunc, sel ...string) *Transformer {
	// TODO come up with a way to walk tree once?
	sq := NewSelectorQuery(sel...)
	nodes := sq.Apply(t.doc)
	for _, n := range nodes {
		f(n)
	}
	return t
}

// Compose a set of TransformFuncs into a single TransformFunc
func Compose(fs ...TransformFunc) TransformFunc {
	return func(n *Node) {
		for _, f := range fs {
			f(n)
		}
	}
}

// AppendChildren creates a TransformFunc that appends the Children passed in.
func AppendChildren(cs ...*Node) TransformFunc {
	return func(n *Node) {
		sz := len(n.Children)
		newChild := make([]*Node, sz+len(cs))
		copy(newChild, n.Children)
		copy(newChild[sz:], cs)
		n.Children = newChild
	}
}

// PrependChildren creates a TransformFunc that prepends the Children passed in.
func PrependChildren(cs ...*Node) TransformFunc {
	return func(n *Node) {
		sz := len(n.Children)
		sz2 := len(cs)
		newChild := make([]*Node, sz+len(cs))
		copy(newChild[sz2:], n.Children)
		copy(newChild[0:sz2], cs)
		n.Children = newChild
	}
}

// RemoveChildren creates a TransformFunc that removes the Children of the node
// it operates on.
func RemoveChildren() TransformFunc {
	return func(n *Node) {
		n.Children = make([]*Node, 0)
	}
}

// ReplaceChildren creates a TransformFunc that replaces the Children of the
// node it operates on with the Children passed in.
func ReplaceChildren(ns ...*Node) TransformFunc {
	return func(n *Node) {
		n.Children = ns
	}
}

func Replace(ns ...*Node) TransformFunc {
	return func(n *Node) {
		p := n.Parent
		switch p {
		case nil:
			n.Children = ns
		default:
			for i, c := range p.Children {
				if c == n {
					n := i - 1
					if n < 0 {
						n = 0
					}
					var newChild []*Node
					pre := p.Children[:n]
					post := p.Children[i+1:]
					newChild = append(pre, ns...)
					p.Children = append(newChild, post...)
				}
			}
		}
	}
}

// ModifyAttrb creates a TransformFunc that modifies the attributes
// of the node it operates on.
func ModifyAttrib(key string, val string) TransformFunc {
	return func(n *Node) {
		found := false
		for i, attr := range n.Attr {
			if attr.Name == key {
				n.Attr[i].Value = val
				found = true
			}
		}
		if !found {
			newAttr := make([]*Attribute, len(n.Attr)+1)
			newAttr[len(n.Attr)] = &Attribute{Name: key, Value: val}
			n.Attr = newAttr
		}
	}
}

func TransformAttrib(key string, f func(string) string) TransformFunc {
	return func(n *Node) {
		for i, attr := range n.Attr {
			if attr.Name == key {
				n.Attr[i].Value = f(n.Attr[i].Value)
			}
		}
	}
}

func DoAll(fs ...TransformFunc) TransformFunc {
	return func(n *Node) {
		for _, f := range fs {
			f(n)
		}
	}
}

// ForEach takes a function and a list of Nodes and performs that
// function for each node in the list.
// The function should be of a type either func(...*Node) TransformFunc
// or func(*Node) TransformFunc. Any other type will panic.
// Returns a TransformFunc.
func ForEach(f interface{}, ns ...*Node) TransformFunc {
	switch t := f.(type) {
	case func(...*Node) TransformFunc:
		return func(n *Node) {
			for _, n2 := range ns {
				f1 := f.(func(...*Node) TransformFunc)
				f2 := f1(n2)
				f2(n)
			}
		}
	case func(*Node) TransformFunc:
		return func(n *Node) {
			for _, n2 := range ns {
				f1 := f.(func(*Node) TransformFunc)
				f2 := f1(n2)
				f2(n)
			}
		}
	default:
		log.Panicf("Wrong function type passed to ForEach %s", t)
	}
	return nil
}

// CopyAnd will construct a TransformFunc that will
// make a copy of the node for each passed in TransformFunc
// And replace the passed in node with the resulting transformed
// Nodes.
// Returns a TransformFunc
func CopyAnd(fns ...TransformFunc) TransformFunc {
	return func(n *Node) {
		newNodes := make([]*Node, len(fns))
		for i, fn := range fns {
			node := n.Clone()
			fn(node)
			newNodes[i] = node
		}
		replaceFn := Replace(newNodes...)
		replaceFn(n)
	}
}
// Copyright 2010 Jeremy Wall (jeremy@marzhillstudios.com)
// Use of this source code is governed by the Artistic License 2.0.
// That License is included in the LICENSE file.
<|endoftext|>


<commit_before>package main_test

import (
	"net"
	"os/exec"

	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gexec"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/ghttp"
)

var _ = Describe("HealthCheck", func() {
	var (
		server     *ghttp.Server
		serverAddr string
	)

	BeforeEach(func() {
		ip := getNonLoopbackIP()
		server = ghttp.NewUnstartedServer()
		listener, err := net.Listen("tcp", ip+":0")
		Expect(err).NotTo(HaveOccurred())

		server.HTTPTestServer.Listener = listener
		serverAddr = listener.Addr().String()
		server.Start()
	})

	runHealthCheck := func() *gexec.Session {
		_, port, err := net.SplitHostPort(serverAddr)
		Expect(err).NotTo(HaveOccurred())
		session, err := gexec.Start(exec.Command(healthcheck, "-port", port, "-timeout", "100ms"), GinkgoWriter, GinkgoWriter)
		Expect(err).NotTo(HaveOccurred())
		return session
	}

	Context("when the address is listening", func() {
		It("exits 0 and logs it passed", func() {
			session := runHealthCheck()
			Eventually(session).Should(gexec.Exit(0))
			Expect(session.Out).To(gbytes.Say("healthcheck passed"))
		})
	})

	Context("when the address is not listening", func() {
		BeforeEach(func() {
			server.Close()
		})

		It("exits 1 and logs a failure", func() {
			session := runHealthCheck()
			Eventually(session).Should(gexec.Exit(1))
			Expect(session.Out).To(gbytes.Say("healthcheck failed"))
		})
	})
})

func getNonLoopbackIP() string {
	interfaces, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())
	for _, intf := range interfaces {
		addrs, err := intf.Addrs()
		if err != nil {
			continue
		}

		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					return ipnet.IP.String()
				}
			}
		}
	}
	Fail("no non-loopback address found")
	panic("non-reachable")
}
<commit_msg>Ensure server is down for failing health test<commit_after>package main_test

import (
	"net"
	"os/exec"

	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gexec"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/ghttp"
)

var _ = Describe("HealthCheck", func() {
	var (
		server     *ghttp.Server
		serverAddr string
	)

	BeforeEach(func() {
		ip := getNonLoopbackIP()
		server = ghttp.NewUnstartedServer()
		listener, err := net.Listen("tcp", ip+":0")
		Expect(err).NotTo(HaveOccurred())

		server.HTTPTestServer.Listener = listener
		serverAddr = listener.Addr().String()
		server.Start()
	})

	runHealthCheck := func() *gexec.Session {
		_, port, err := net.SplitHostPort(serverAddr)
		Expect(err).NotTo(HaveOccurred())
		session, err := gexec.Start(exec.Command(healthcheck, "-port", port, "-timeout", "100ms"), GinkgoWriter, GinkgoWriter)
		Expect(err).NotTo(HaveOccurred())
		return session
	}

	Context("when the address is listening", func() {
		It("exits 0 and logs it passed", func() {
			session := runHealthCheck()
			Eventually(session).Should(gexec.Exit(0))
			Expect(session.Out).To(gbytes.Say("healthcheck passed"))
		})
	})

	Context("when the address is not listening", func() {
		BeforeEach(func() {
			server.Close()
			Eventually(func() error {
				_, err := net.Dial("tcp", serverAddr)
				return err
			}).Should(HaveOccurred())
		})

		It("exits 1 and logs a failure", func() {
			session := runHealthCheck()
			Eventually(session).Should(gexec.Exit(1))
			Expect(session.Out).To(gbytes.Say("healthcheck failed"))
		})
	})
})

func getNonLoopbackIP() string {
	interfaces, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())
	for _, intf := range interfaces {
		addrs, err := intf.Addrs()
		if err != nil {
			continue
		}

		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					return ipnet.IP.String()
				}
			}
		}
	}
	Fail("no non-loopback address found")
	panic("non-reachable")
}
<|endoftext|>


<commit_before>package Components

import (
	"github.com/go-gl/gl"
	//"image"
	//"github.com/go-gl/glfw"
	//"gl/glu"
	//"log"

	//"bufio"
	//"image/png"
	//"os"
	//"strconv"
	//"github.com/go-gl/glfw"
	"github.com/vova616/GarageEngine/Engine"
	"github.com/vova616/GarageEngine/Engine/Input"
	"github.com/vova616/chipmunk/vect"
	//"runtime"
)

type UIText struct {
	Engine.BaseComponent
	Font           *Engine.Font
	text           string
	buffer         gl.Buffer
	vertexCount    int
	texcoordsIndex int

	tabSize int

	width  float32
	height float32
	align  Engine.AlignType

	focused    bool
	writeable  bool
	updateText bool

	autoFocus bool //This will go away

	Color Engine.Vector
}

func NewUIText(font *Engine.Font, text string) *UIText {
	if font == nil {
		return nil
	}

	uitext := &UIText{BaseComponent: Engine.NewComponent(),
		Font:      font,
		text:      text,
		buffer:    gl.GenBuffer(),
		align:     Engine.AlignCenter,
		writeable: false,
		tabSize:   4,
		Color:     Engine.Vector{1, 1, 1}}

	uitext.setString(text)
	Input.AddCharCallback(func(rn rune) { uitext.charCallback(rn) })
	return uitext
}

func (ui *UIText) OnComponentBind(binded *Engine.GameObject) {
	ph := binded.AddComponent(Engine.NewPhysics(false, 1, 1)).(*Engine.Physics)
	_ = ph
	ph.Body.IgnoreGravity = true
	ph.Shape.IsSensor = true
}

func (ui *UIText) Width() float32 {
	return ui.width
}

func (ui *UIText) Height() float32 {
	return ui.height
}

func (ui *UIText) SetString(text string) {
	ui.text = text
	ui.updateText = true
}

func (ui *UIText) setString(text string) {
	ui.text = text

	if text == "" {
		ui.buffer.Bind(gl.ARRAY_BUFFER)
		gl.BufferData(gl.ARRAY_BUFFER, 1, []byte{1}, gl.STATIC_DRAW)
		return
	}

	l := len(text)
	lt := l * 12
	_ = lt
	data := make([]float32, 20*l)

	vertexCount := 0
	texcoordsIndex := lt * 4

	space := float32(0)
	w, h := ui.GetPixelSize(text)

	index := 0
	for _, rune := range text {
		spaceMult := float32(1)
		if rune == '\t' {
			rune = ' '
			spaceMult = float32(index % ui.tabSize)
		}
		atlasImage := ui.Font.LetterInfo(rune)

		if atlasImage == nil {
			continue
		}

		yratio := atlasImage.RelativeHeight
		xratio := atlasImage.RelativeWidth

		//ygrid := -0.5 + (atlasImage.YGrid)
		//xgrid := (-0.5 + (atlasImage.XGrid)) + space
		ygrid := -(h / 2) + (atlasImage.YOffset)
		xgrid := -(w / 2) + (atlasImage.XOffset) + space
		space += atlasImage.XAdvance * spaceMult

		vertexCount += 4

		uv := Engine.IndexUV(ui.Font, rune)

		data[(index*12)+0] = xgrid
		data[(index*12)+1] = ygrid
		data[(index*12)+2] = 1
		data[(index*12)+3] = (xratio) + xgrid
		data[(index*12)+4] = ygrid
		data[(index*12)+5] = 1
		data[(index*12)+6] = (xratio) + xgrid
		data[(index*12)+7] = (yratio) + ygrid
		data[(index*12)+8] = 1
		data[(index*12)+9] = xgrid
		data[(index*12)+10] = (yratio) + ygrid
		data[(index*12)+11] = 1

		data[lt+(index*8)+0] = uv.U1
		data[lt+(index*8)+1] = uv.V2
		data[lt+(index*8)+2] = uv.U2
		data[lt+(index*8)+3] = uv.V2

		data[lt+(index*8)+4] = uv.U2
		data[lt+(index*8)+5] = uv.V1
		data[lt+(index*8)+6] = uv.U1
		data[lt+(index*8)+7] = uv.V1
		index++
	}

	ui.width = w
	ui.height = h

	ui.vertexCount = vertexCount
	ui.texcoordsIndex = texcoordsIndex
	ui.buffer.Bind(gl.ARRAY_BUFFER)
	gl.BufferData(gl.ARRAY_BUFFER, len(data)*4, data, gl.STATIC_DRAW)
}

func (ui *UIText) GetPixelSize(text string) (width float32, height float32) {
	index := 0
	for _, rune := range text {
		spaceMult := float32(1)
		if rune == '\t' {
			rune = ' '
			spaceMult = float32(index % ui.tabSize)
		}
		atlasImage := ui.Font.LetterInfo(rune)

		if atlasImage == nil {
			continue
		}
		width += atlasImage.RelativeWidth * spaceMult
		/*
			yratio := atlasImage.PlaneHeight
			ygrid := atlasImage.YGrid
			if yratio < 0 {
				yratio = -yratio
			}
			if ygrid < 0 {
				ygrid = -ygrid
			}

			if yratio+ygrid > height {
				height = yratio + ygrid
			}
		*/
		height = 1
	}
	return
}

func (ui *UIText) String() string {
	return ui.text
}

func (ui *UIText) Start() {

}

func (ui *UIText) SetFocus(b bool) {
	ui.focused = b
}
func (ui *UIText) SetWritable(b bool) {
	ui.writeable = b
}

func (ui *UIText) charCallback(rn rune) {
	if ui.focused && ui.writeable {
		ui.text += string(rn)
		ui.updateText = true
	}
}

//var speed = float32(30)

func (ui *UIText) Update() {
	ui.UpdateCollider()

	/*
		s := ui.Transform().Scale()

		if s.X < 20 && speed < 0 {
			speed = -speed
		} else if s.X > 200 && speed > 0 {
			speed = -speed
		}

		s.X += speed * Engine.DeltaTime()
		s.Y += speed * Engine.DeltaTime()

		ui.Transform().SetScale(s)
	*/

	//Handle Tab & Backspace
	if ui.focused && ui.writeable {
		if len(ui.text) > 0 && Input.KeyPress(Input.KeyBackspace) {
			ui.updateText = true
			ui.text = ui.text[:len(ui.text)-1]
		}
		if Input.KeyPress(Input.KeyTab) {
			ui.updateText = true
			ui.text += "\t"
		}
	}
}

func (ui *UIText) LateUpdate() {
	if ui.updateText {
		ui.updateText = false
		ui.setString(ui.text)
	}
}

func (ui *UIText) UpdateCollider() {
	//if ui.GameObject().Physics.Body.Enabled {
	if ui.GameObject().Physics == nil {
		return
	}
	b := ui.GameObject().Physics.Box
	if b != nil {
		h := vect.Float(float64(ui.height) * float64(ui.GameObject().Transform().WorldScale().Y))
		w := vect.Float(float64(ui.width) * float64(ui.GameObject().Transform().WorldScale().X))
		update := false
		if h != b.Height || w != b.Width {
			b.Width = w
			b.Height = h
			update = true
		}

		c := Engine.Align(ui.align)
		center := vect.Vect{vect.Float(c.X), vect.Float(c.Y)}
		center.X = (center.X * w)
		center.Y = (center.Y * h)

		if b.Position.X != center.X || b.Position.Y != center.Y {
			update = true
			b.Position.X, b.Position.Y = center.X, center.Y
		}

		if update {
			b.UpdatePoly()
		}
	}
	//log.Println(b.Height, b.Width, ui.GameObject().Transform().Scale().X, ui.GameObject().Name())
	//}
}

func (ui *UIText) Align() Engine.AlignType {
	return ui.align
}

func (ui *UIText) SetAlign(align Engine.AlignType) {
	ui.align = align
}

func (ui *UIText) Draw() {
	if ui.text == "" {
		return
	}

	v := Engine.Align(ui.align)
	v.X = (v.X * ui.width)
	v.Y = (v.Y * ui.height)

	mat := Engine.TextureMaterial
	if ui.Font.IsSDF() {
		mat = Engine.SDFMaterial
	}
	mat.Begin(ui.GameObject())

	vert := mat.Verts
	uv := mat.UV
	mp := mat.ProjMatrix
	mv := mat.ViewMatrix
	mm := mat.ModelMatrix
	tx := mat.Texture
	color := mat.AddColor
	_ = color

	vert.EnableArray()
	uv.EnableArray()

	ui.buffer.Bind(gl.ARRAY_BUFFER)

	vert.AttribPointer(3, gl.FLOAT, false, 0, uintptr(0))
	uv.AttribPointer(2, gl.FLOAT, false, 0, uintptr(ui.texcoordsIndex))

	camera := Engine.GetScene().SceneBase().Camera

	view := camera.Transform().Matrix()
	view = view.Invert()
	model := Engine.NewIdentity()
	model.Translate(v.X, v.Y, 0)
	model.Mul(ui.GameObject().Transform().Matrix())

	/*
		view := camera.Transform().Matrix()
		view = view.Invert()
		model := ui.GameObject().Transform().Matrix()
	*/

	mv.UniformMatrix4fv(false, view)
	mp.UniformMatrix4fv(false, *camera.Projection)
	mm.UniformMatrix4fv(false, *model)

	ui.Font.Bind()
	gl.ActiveTexture(gl.TEXTURE0)
	tx.Uniform1i(0)

	color.Uniform4f(ui.Color.X, ui.Color.Y, ui.Color.Z, 1)

	gl.DrawArrays(gl.QUADS, 0, ui.vertexCount)

	ui.Font.Unbind()
	vert.DisableArray()
	uv.DisableArray()

}
<commit_msg>Fixed GetPixelSize.<commit_after>package Components

import (
	"github.com/go-gl/gl"
	//"image"
	//"github.com/go-gl/glfw"
	//"gl/glu"
	//"log"

	//"bufio"
	//"image/png"
	//"os"
	//"strconv"
	//"github.com/go-gl/glfw"
	"github.com/vova616/GarageEngine/Engine"
	"github.com/vova616/GarageEngine/Engine/Input"
	"github.com/vova616/chipmunk/vect"
	//"runtime"
)

type UIText struct {
	Engine.BaseComponent
	Font           *Engine.Font
	text           string
	buffer         gl.Buffer
	vertexCount    int
	texcoordsIndex int

	tabSize int

	width  float32
	height float32
	align  Engine.AlignType

	focused    bool
	writeable  bool
	updateText bool

	autoFocus bool //This will go away

	Color Engine.Vector
}

func NewUIText(font *Engine.Font, text string) *UIText {
	if font == nil {
		return nil
	}

	uitext := &UIText{BaseComponent: Engine.NewComponent(),
		Font:      font,
		text:      text,
		buffer:    gl.GenBuffer(),
		align:     Engine.AlignCenter,
		writeable: false,
		tabSize:   4,
		Color:     Engine.Vector{1, 1, 1}}

	uitext.setString(text)
	Input.AddCharCallback(func(rn rune) { uitext.charCallback(rn) })
	return uitext
}

func (ui *UIText) OnComponentBind(binded *Engine.GameObject) {
	ph := binded.AddComponent(Engine.NewPhysics(false, 1, 1)).(*Engine.Physics)
	_ = ph
	ph.Body.IgnoreGravity = true
	ph.Shape.IsSensor = true
}

func (ui *UIText) Width() float32 {
	return ui.width
}

func (ui *UIText) Height() float32 {
	return ui.height
}

func (ui *UIText) SetString(text string) {
	ui.text = text
	ui.updateText = true
}

func (ui *UIText) setString(text string) {
	ui.text = text

	if text == "" {
		ui.buffer.Bind(gl.ARRAY_BUFFER)
		gl.BufferData(gl.ARRAY_BUFFER, 1, []byte{1}, gl.STATIC_DRAW)
		return
	}

	l := len(text)
	lt := l * 12
	_ = lt
	data := make([]float32, 20*l)

	vertexCount := 0
	texcoordsIndex := lt * 4

	space := float32(0)
	w, h := ui.GetPixelSize(text)

	index := 0
	for _, rune := range text {
		spaceMult := float32(1)
		if rune == '\t' {
			rune = ' '
			spaceMult = float32(index % ui.tabSize)
		}
		atlasImage := ui.Font.LetterInfo(rune)

		if atlasImage == nil {
			continue
		}

		yratio := atlasImage.RelativeHeight
		xratio := atlasImage.RelativeWidth

		//ygrid := -0.5 + (atlasImage.YGrid)
		//xgrid := (-0.5 + (atlasImage.XGrid)) + space
		ygrid := -(h / 2) + (atlasImage.YOffset)
		xgrid := -(w / 2) + (atlasImage.XOffset) + space
		space += atlasImage.XAdvance * spaceMult

		vertexCount += 4

		uv := Engine.IndexUV(ui.Font, rune)

		data[(index*12)+0] = xgrid
		data[(index*12)+1] = ygrid
		data[(index*12)+2] = 1
		data[(index*12)+3] = (xratio) + xgrid
		data[(index*12)+4] = ygrid
		data[(index*12)+5] = 1
		data[(index*12)+6] = (xratio) + xgrid
		data[(index*12)+7] = (yratio) + ygrid
		data[(index*12)+8] = 1
		data[(index*12)+9] = xgrid
		data[(index*12)+10] = (yratio) + ygrid
		data[(index*12)+11] = 1

		data[lt+(index*8)+0] = uv.U1
		data[lt+(index*8)+1] = uv.V2
		data[lt+(index*8)+2] = uv.U2
		data[lt+(index*8)+3] = uv.V2

		data[lt+(index*8)+4] = uv.U2
		data[lt+(index*8)+5] = uv.V1
		data[lt+(index*8)+6] = uv.U1
		data[lt+(index*8)+7] = uv.V1
		index++
	}

	ui.width = w
	ui.height = h

	ui.vertexCount = vertexCount
	ui.texcoordsIndex = texcoordsIndex
	ui.buffer.Bind(gl.ARRAY_BUFFER)
	gl.BufferData(gl.ARRAY_BUFFER, len(data)*4, data, gl.STATIC_DRAW)
}

func (ui *UIText) GetPixelSize(text string) (width float32, height float32) {
	index := 0
	for _, rune := range text {
		spaceMult := float32(1)
		if rune == '\t' {
			rune = ' '
			spaceMult = float32(index % ui.tabSize)
		}
		atlasImage := ui.Font.LetterInfo(rune)

		if atlasImage == nil {
			continue
		}
		width += atlasImage.XAdvance * spaceMult
		/*
			yratio := atlasImage.PlaneHeight
			ygrid := atlasImage.YGrid
			if yratio < 0 {
				yratio = -yratio
			}
			if ygrid < 0 {
				ygrid = -ygrid
			}

			if yratio+ygrid > height {
				height = yratio + ygrid
			}
		*/
		height = 1
	}
	return
}

func (ui *UIText) String() string {
	return ui.text
}

func (ui *UIText) Start() {

}

func (ui *UIText) SetFocus(b bool) {
	ui.focused = b
}
func (ui *UIText) SetWritable(b bool) {
	ui.writeable = b
}

func (ui *UIText) charCallback(rn rune) {
	if ui.focused && ui.writeable {
		ui.text += string(rn)
		ui.updateText = true
	}
}

//var speed = float32(30)

func (ui *UIText) Update() {
	ui.UpdateCollider()

	/*
		s := ui.Transform().Scale()

		if s.X < 20 && speed < 0 {
			speed = -speed
		} else if s.X > 200 && speed > 0 {
			speed = -speed
		}

		s.X += speed * Engine.DeltaTime()
		s.Y += speed * Engine.DeltaTime()

		ui.Transform().SetScale(s)
	*/

	//Handle Tab & Backspace
	if ui.focused && ui.writeable {
		if len(ui.text) > 0 && Input.KeyPress(Input.KeyBackspace) {
			ui.updateText = true
			ui.text = ui.text[:len(ui.text)-1]
		}
		if Input.KeyPress(Input.KeyTab) {
			ui.updateText = true
			ui.text += "\t"
		}
	}
}

func (ui *UIText) LateUpdate() {
	if ui.updateText {
		ui.updateText = false
		ui.setString(ui.text)
	}
}

func (ui *UIText) UpdateCollider() {
	//if ui.GameObject().Physics.Body.Enabled {
	if ui.GameObject().Physics == nil {
		return
	}
	b := ui.GameObject().Physics.Box
	if b != nil {
		h := vect.Float(float64(ui.height) * float64(ui.GameObject().Transform().WorldScale().Y))
		w := vect.Float(float64(ui.width) * float64(ui.GameObject().Transform().WorldScale().X))
		update := false
		if h != b.Height || w != b.Width {
			b.Width = w
			b.Height = h
			update = true
		}

		c := Engine.Align(ui.align)
		center := vect.Vect{vect.Float(c.X), vect.Float(c.Y)}
		center.X = (center.X * w)
		center.Y = (center.Y * h)

		if b.Position.X != center.X || b.Position.Y != center.Y {
			update = true
			b.Position.X, b.Position.Y = center.X, center.Y
		}

		if update {
			b.UpdatePoly()
		}
	}
	//log.Println(b.Height, b.Width, ui.GameObject().Transform().Scale().X, ui.GameObject().Name())
	//}
}

func (ui *UIText) Align() Engine.AlignType {
	return ui.align
}

func (ui *UIText) SetAlign(align Engine.AlignType) {
	ui.align = align
}

func (ui *UIText) Draw() {
	if ui.text == "" {
		return
	}

	v := Engine.Align(ui.align)
	v.X = (v.X * ui.width)
	v.Y = (v.Y * ui.height)

	mat := Engine.TextureMaterial
	if ui.Font.IsSDF() {
		mat = Engine.SDFMaterial
	}
	mat.Begin(ui.GameObject())

	vert := mat.Verts
	uv := mat.UV
	mp := mat.ProjMatrix
	mv := mat.ViewMatrix
	mm := mat.ModelMatrix
	tx := mat.Texture
	color := mat.AddColor
	_ = color

	vert.EnableArray()
	uv.EnableArray()

	ui.buffer.Bind(gl.ARRAY_BUFFER)

	vert.AttribPointer(3, gl.FLOAT, false, 0, uintptr(0))
	uv.AttribPointer(2, gl.FLOAT, false, 0, uintptr(ui.texcoordsIndex))

	camera := Engine.GetScene().SceneBase().Camera

	view := camera.Transform().Matrix()
	view = view.Invert()
	model := Engine.NewIdentity()
	model.Translate(v.X, v.Y, 0)
	model.Mul(ui.GameObject().Transform().Matrix())

	/*
		view := camera.Transform().Matrix()
		view = view.Invert()
		model := ui.GameObject().Transform().Matrix()
	*/

	mv.UniformMatrix4fv(false, view)
	mp.UniformMatrix4fv(false, *camera.Projection)
	mm.UniformMatrix4fv(false, *model)

	ui.Font.Bind()
	gl.ActiveTexture(gl.TEXTURE0)
	tx.Uniform1i(0)

	color.Uniform4f(ui.Color.X, ui.Color.Y, ui.Color.Z, 1)

	gl.DrawArrays(gl.QUADS, 0, ui.vertexCount)

	ui.Font.Unbind()
	vert.DisableArray()
	uv.DisableArray()

}
<|endoftext|>


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

package facade

import (
	"github.com/control-center/serviced/dao"
	"github.com/control-center/serviced/datastore"
	"github.com/control-center/serviced/domain/addressassignment"
	"github.com/control-center/serviced/domain/host"
	"github.com/control-center/serviced/domain/pool"
	"github.com/control-center/serviced/domain/service"
	"github.com/control-center/serviced/domain/servicedefinition"
	"github.com/zenoss/glog"
	. "gopkg.in/check.v1"
)

func (s *FacadeTest) Test_HostCRUD(t *C) {
	testid := "deadb10f"
	poolid := "pool-id"
	defer s.Facade.RemoveHost(s.CTX, testid)

	//fill host with required values
	h, err := host.Build("", "65535", poolid, []string{}...)
	h.ID = testid
	if err != nil {
		t.Fatalf("Unexpected error building host: %v", err)
	}
	glog.Infof("Facade test add host %v", h)
	err = s.Facade.AddHost(s.CTX, h)
	//should fail since pool doesn't exist
	if err == nil {
		t.Errorf("Expected error: %v", err)
	}

	//create pool for test
	rp := pool.New(poolid)
	if err := s.Facade.AddResourcePool(s.CTX, rp); err != nil {
		t.Fatalf("Could not add pool for test: %v", err)
	}
	defer s.Facade.RemoveResourcePool(s.CTX, poolid)

	err = s.Facade.AddHost(s.CTX, h)
	if err != nil {
		t.Errorf("Unexpected error adding host: %v", err)
	}

	//Test re-add fails
	err = s.Facade.AddHost(s.CTX, h)
	if err == nil {
		t.Errorf("Expected already exists error: %v", err)
	}

	h2, err := s.Facade.GetHost(s.CTX, testid)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	if h2 == nil {
		t.Error("Unexpected nil host")

	} else if !host.HostEquals(t, h, h2) {
		t.Error("Hosts did not match")
	}

	//Test update
	h.Memory = 1024
	err = s.Facade.UpdateHost(s.CTX, h)
	h2, err = s.Facade.GetHost(s.CTX, testid)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	if !host.HostEquals(t, h, h2) {
		t.Error("Hosts did not match")
	}

	//test delete
	err = s.Facade.RemoveHost(s.CTX, testid)
	h2, err = s.Facade.GetHost(s.CTX, testid)
	if err != nil && !datastore.IsErrNoSuchEntity(err) {
		t.Errorf("Unexpected error: %v", err)
	}
}

func (s *FacadeTest) Test_HostRemove(t *C) {
	//create pool for testing
	resoucePool := pool.New("poolid")
	s.Facade.AddResourcePool(s.CTX, resoucePool)
	defer s.Facade.RemoveResourcePool(s.CTX, "poolid")

	//add host1
	h1 := host.Host{
		ID:      "deadb11f",
		PoolID:  "poolid",
		Name:    "h1",
		IPAddr:  "192.168.0.1",
		RPCPort: 65535,
		IPs: []host.HostIPResource{
			host.HostIPResource{
				HostID:    "deadb11f",
				IPAddress: "192.168.0.1",
			},
		},
	}
	err := s.Facade.AddHost(s.CTX, &h1)
	if err != nil {
		t.Fatalf("Failed to add host %+v: %s", h1, err)
	}

	//add host2
	h2 := host.Host{
		ID:      "deadb12f",
		PoolID:  "poolid",
		Name:    "h2",
		IPAddr:  "192.168.0.2",
		RPCPort: 65535,
		IPs: []host.HostIPResource{
			host.HostIPResource{
				HostID:    "deadb12f",
				IPAddress: "192.168.0.2",
			},
		},
	}
	err = s.Facade.AddHost(s.CTX, &h2)
	if err != nil {
		t.Fatalf("Failed to add host %+v: %s", h2, err)
	}
	defer s.Facade.RemoveHost(s.CTX, "host2")

	//add service with address assignment
	s1, _ := service.NewService()
	s1.Name = "name"
	s1.PoolID = "poolid"
	s1.Launch = "manual"
	s1.Endpoints = []service.ServiceEndpoint{
		service.ServiceEndpoint{},
	}
	s1.Endpoints[0].Name = "name"
	s1.Endpoints[0].AddressConfig = servicedefinition.AddressResourceConfig{Port: 123, Protocol: "tcp"}
	aa := addressassignment.AddressAssignment{ID: "id", HostID: h1.ID}
	s1.Endpoints[0].SetAssignment(aa)
	err = s.Facade.AddService(s.CTX, *s1)
	if err != nil {
		t.Fatalf("Failed to add service %+v: %s", s1, err)
	}
	defer s.Facade.RemoveService(s.CTX, s1.ID)

	request := dao.AssignmentRequest{ServiceID: s1.ID, IPAddress: h1.IPAddr, AutoAssignment: false}
	if err = s.Facade.AssignIPs(s.CTX, request); err != nil {
		t.Fatalf("Failed assigning ip to service: %s", err)
	}

	var serviceRequest dao.ServiceRequest
	services, _ := s.Facade.GetServices(s.CTX, serviceRequest)
	if len(services) <= 0 {
		t.Fatalf("Expected one service in context")
	}
	if len(services[0].Endpoints) <= 0 {
		t.Fatalf("Expected service with one endpoint in context")
	}
	ep := services[0].Endpoints[0]
	if ep.AddressAssignment.IPAddr != h1.IPAddr && ep.AddressAssignment.HostID != h1.ID {
		t.Fatalf("Incorrect IPAddress and HostID before remove host")
	}

	//remove host1
	err = s.Facade.RemoveHost(s.CTX, h1.ID)
	if err != nil {
		t.Fatalf("Failed to remove host: %s", err)
	}
	services, _ = s.Facade.GetServices(s.CTX, serviceRequest)

	if len(services) <= 0 {
		t.Fatalf("Expected one service in context")
	}

	if len(services[0].Endpoints) <= 0 {
		t.Fatalf("Expected service with one endpoint in context")
	}

	ep = services[0].Endpoints[0]
	if ep.AddressAssignment.IPAddr == h2.IPAddr || ep.AddressAssignment.HostID == h2.ID {
		t.Fatalf("Incorrect IPAddress and HostID after remove host")
	}
}
<commit_msg>fixed facade test<commit_after>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package facade

import (
	"github.com/control-center/serviced/dao"
	"github.com/control-center/serviced/datastore"
	"github.com/control-center/serviced/domain/addressassignment"
	"github.com/control-center/serviced/domain/host"
	"github.com/control-center/serviced/domain/pool"
	"github.com/control-center/serviced/domain/service"
	"github.com/control-center/serviced/domain/servicedefinition"
	"github.com/zenoss/glog"
	. "gopkg.in/check.v1"
)

func (s *FacadeTest) Test_HostCRUD(t *C) {
	testid := "deadb10f"
	poolid := "pool-id"
	defer s.Facade.RemoveHost(s.CTX, testid)

	//fill host with required values
	h, err := host.Build("", "65535", poolid, []string{}...)
	h.ID = testid
	if err != nil {
		t.Fatalf("Unexpected error building host: %v", err)
	}
	glog.Infof("Facade test add host %v", h)
	err = s.Facade.AddHost(s.CTX, h)
	//should fail since pool doesn't exist
	if err == nil {
		t.Errorf("Expected error: %v", err)
	}

	//create pool for test
	rp := pool.New(poolid)
	if err := s.Facade.AddResourcePool(s.CTX, rp); err != nil {
		t.Fatalf("Could not add pool for test: %v", err)
	}
	defer s.Facade.RemoveResourcePool(s.CTX, poolid)

	err = s.Facade.AddHost(s.CTX, h)
	if err != nil {
		t.Errorf("Unexpected error adding host: %v", err)
	}

	//Test re-add fails
	err = s.Facade.AddHost(s.CTX, h)
	if err == nil {
		t.Errorf("Expected already exists error: %v", err)
	}

	h2, err := s.Facade.GetHost(s.CTX, testid)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	if h2 == nil {
		t.Error("Unexpected nil host")

	} else if !host.HostEquals(t, h, h2) {
		t.Error("Hosts did not match")
	}

	//Test update
	h.Memory = 1024
	err = s.Facade.UpdateHost(s.CTX, h)
	h2, err = s.Facade.GetHost(s.CTX, testid)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	if !host.HostEquals(t, h, h2) {
		t.Error("Hosts did not match")
	}

	//test delete
	err = s.Facade.RemoveHost(s.CTX, testid)
	h2, err = s.Facade.GetHost(s.CTX, testid)
	if err != nil && !datastore.IsErrNoSuchEntity(err) {
		t.Errorf("Unexpected error: %v", err)
	}
}

func (s *FacadeTest) Test_HostRemove(t *C) {
	//create pool for testing
	resoucePool := pool.New("poolid")
	s.Facade.AddResourcePool(s.CTX, resoucePool)
	defer s.Facade.RemoveResourcePool(s.CTX, "poolid")

	//add host1
	h1 := host.Host{
		ID:      "deadb11f",
		PoolID:  "poolid",
		Name:    "h1",
		IPAddr:  "192.168.0.1",
		RPCPort: 65535,
		IPs: []host.HostIPResource{
			host.HostIPResource{
				HostID:    "deadb11f",
				IPAddress: "192.168.0.1",
			},
		},
	}
	err := s.Facade.AddHost(s.CTX, &h1)
	if err != nil {
		t.Fatalf("Failed to add host %+v: %s", h1, err)
	}

	//add host2
	h2 := host.Host{
		ID:      "deadb12f",
		PoolID:  "poolid",
		Name:    "h2",
		IPAddr:  "192.168.0.2",
		RPCPort: 65535,
		IPs: []host.HostIPResource{
			host.HostIPResource{
				HostID:    "deadb12f",
				IPAddress: "192.168.0.2",
			},
		},
	}
	err = s.Facade.AddHost(s.CTX, &h2)
	if err != nil {
		t.Fatalf("Failed to add host %+v: %s", h2, err)
	}
	defer s.Facade.RemoveHost(s.CTX, "host2")

	//add service with address assignment
	s1, _ := service.NewService()
	s1.Name = "name"
	s1.PoolID = "poolid"
	s1.DeploymentID = "deployment_id"
	s1.Launch = "manual"
	s1.Endpoints = []service.ServiceEndpoint{
		service.ServiceEndpoint{},
	}
	s1.Endpoints[0].Name = "name"
	s1.Endpoints[0].AddressConfig = servicedefinition.AddressResourceConfig{Port: 123, Protocol: "tcp"}
	aa := addressassignment.AddressAssignment{ID: "id", HostID: h1.ID}
	s1.Endpoints[0].SetAssignment(aa)
	err = s.Facade.AddService(s.CTX, *s1)
	if err != nil {
		t.Fatalf("Failed to add service %+v: %s", s1, err)
	}
	defer s.Facade.RemoveService(s.CTX, s1.ID)

	request := dao.AssignmentRequest{ServiceID: s1.ID, IPAddress: h1.IPAddr, AutoAssignment: false}
	if err = s.Facade.AssignIPs(s.CTX, request); err != nil {
		t.Fatalf("Failed assigning ip to service: %s", err)
	}

	var serviceRequest dao.ServiceRequest
	services, _ := s.Facade.GetServices(s.CTX, serviceRequest)
	if len(services) <= 0 {
		t.Fatalf("Expected one service in context")
	}
	if len(services[0].Endpoints) <= 0 {
		t.Fatalf("Expected service with one endpoint in context")
	}
	ep := services[0].Endpoints[0]
	if ep.AddressAssignment.IPAddr != h1.IPAddr && ep.AddressAssignment.HostID != h1.ID {
		t.Fatalf("Incorrect IPAddress and HostID before remove host")
	}

	//remove host1
	err = s.Facade.RemoveHost(s.CTX, h1.ID)
	if err != nil {
		t.Fatalf("Failed to remove host: %s", err)
	}
	services, _ = s.Facade.GetServices(s.CTX, serviceRequest)

	if len(services) <= 0 {
		t.Fatalf("Expected one service in context")
	}

	if len(services[0].Endpoints) <= 0 {
		t.Fatalf("Expected service with one endpoint in context")
	}

	ep = services[0].Endpoints[0]
	if ep.AddressAssignment.IPAddr == h2.IPAddr || ep.AddressAssignment.HostID == h2.ID {
		t.Fatalf("Incorrect IPAddress and HostID after remove host")
	}
}
<|endoftext|>