<commit_before>package dkrcrypt

// The HIGHT Block cipher from KISA
// Copyright (c) 2012 Damian Gryski <damian@gryski.com>
// Licensed under the GPLv3 or, at your option, any later version.

/*

References:

http://tools.ietf.org/html/draft-kisa-hight-00
http://www.iacr.org/cryptodb/archive/2006/CHES/04/04.pdf
http://seed.kisa.or.kr/kor/hight/hightInfo.jsp

*/

import (
	"strconv"
)

// A HightCipher is an instance of HIGHT encryption using a particular key.
type HightCipher struct {
	wk [8]byte   // whitened keys
	sk [128]byte // subkeys
}

type KeySizeError int

func (k KeySizeError) Error() string {
	return "dkrcrypt/hight: invalid key size " + strconv.Itoa(int(k))
}

// NewHight creates and returns a new HightCipher.
// The key argument should be 16 bytes.
func NewHight(key []byte) (*HightCipher, error) {
	c := new(HightCipher)

	if klen := len(key); klen != 16 {
		return nil, KeySizeError(len(key))
	}

	whiten(key, c.wk[:])
	subkey(key, c.sk[:])
	return c, nil
}

// Reset zeros the key data so that it will no longer appear in the process' memory.
func (c *HightCipher) Reset() {
	for i := range wk {
		wk[i] = 0
	}

	for i := range sk {
		sk[i] = 0
	}

}

// BlockSize returns the Hight block size.  It is needed to satisfy the Block interface in crypto/cipher.
func (c *HightCipher) BlockSize() int { return 8 }

// rotate left
func rotl(x byte, r byte) byte {
	return (x << r) | (x >> (8 - r))
}

// F0, from the specification
func f0(x byte) byte {
	return rotl(x, 1) ^ rotl(x, 2) ^ rotl(x, 7)
}

// F1, from the specification
func f1(x byte) byte {
	return rotl(x, 3) ^ rotl(x, 4) ^ rotl(x, 6)
}

// Encrypt encrypts the 8-byte block in src and stores the resulting ciphertext in dst.
func (c *HightCipher) Encrypt(dst, src []byte) {

	// numbering looks off here, because the plaintext is stored msb, but
	// having lsb makes our life easier
	x := [...]byte{
		src[7] + c.wk[0], // p0
		src[6],           // p1
		src[5] ^ c.wk[1], // p2
		src[4],           // p3
		src[3] + c.wk[2], // p4
		src[2],           // p5
		src[1] ^ c.wk[3], // p6
		src[0],           // p0
	}

	for i := 0; i < 31; i++ {
		x00 := x[7] ^ (f0(x[6]) + c.sk[4*i+3])
		x[7] = x[6]
		x[6] = x[5] + (f1(x[4]) ^ c.sk[4*i+2])
		x[5] = x[4]
		x[4] = x[3] ^ (f0(x[2]) + c.sk[4*i+1])
		x[3] = x[2]
		x[2] = x[1] + (f1(x[0]) ^ c.sk[4*i+0])
		x[1] = x[0]
		x[0] = x00
	}

	// last round
	dst[6] = x[1] + (f1(x[0]) ^ c.sk[124])
	dst[4] = x[3] ^ (f0(x[2]) + c.sk[125])
	dst[2] = x[5] + (f1(x[4]) ^ c.sk[126])
	dst[0] = x[7] ^ (f0(x[6]) + c.sk[127])

	// whitening
	dst[7] = x[0] + c.wk[4]
	dst[5] = x[2] ^ c.wk[5]
	dst[3] = x[4] + c.wk[6]
	dst[1] = x[6] ^ c.wk[7]
}

// Decrypt decrypts the 8-byte block in src and stores the resulting plaintext in dst.
func (c *HightCipher) Decrypt(dst, src []byte) {

	// whitening
	x := [...]byte{
		src[7] - c.wk[4], // c0
		src[6],           // c1
		src[5] ^ c.wk[5], // c2
		src[4],           // c3
		src[3] - c.wk[6], // c4
		src[2],           // c5
		src[1] ^ c.wk[7], // c6
		src[0],           // c7
	}

	// undo last round
	x[1] = x[1] - (f1(x[0]) ^ c.sk[124])
	x[3] = x[3] ^ (f0(x[2]) + c.sk[125])
	x[5] = x[5] - (f1(x[4]) ^ c.sk[126])
	x[7] = x[7] ^ (f0(x[6]) + c.sk[127])

	for i := 30; i >= 0; i-- {
		x00 := x[0]
		x[0] = x[1]
		x[1] = x[2] - (f1(x[1]) ^ c.sk[4*i+0])
		x[2] = x[3]
		x[3] = x[4] ^ (f0(x[3]) + c.sk[4*i+1])
		x[4] = x[5]
		x[5] = x[6] - (f1(x[5]) ^ c.sk[4*i+2])
		x[6] = x[7]
		x[7] = x00 ^ (f0(x[7]) + c.sk[4*i+3])
	}

	// undo initial whitening
	dst[7] = x[0] - c.wk[0] // p0
	dst[6] = x[1]           // p1
	dst[5] = x[2] ^ c.wk[1] // p2
	dst[4] = x[3]           // p3
	dst[3] = x[4] - c.wk[2] // p4
	dst[2] = x[5]           // p5
	dst[1] = x[6] ^ c.wk[3] // p6
	dst[0] = x[7]           // p7
}

func whiten(mk, wk []byte) {

	for i := 0; i < 4; i++ {
		wk[i] = mk[16-i-12-1]
	}

	for i := 4; i < 8; i++ {
		wk[i] = mk[16-i+4-1]
	}
}

// This table doesn't change, so rather that recompute it every time we need to
// compute subkeys, we just build it once.  The code to create it was:

/*
   s = 0x5A
   d[0] = s
   for i := 1; i < 128; i++ {
           s = (s >> 1) | (((s & 1) ^ ((s & (1 << 3)) >> 3)) << 6)
           d[i] = s
   }
*/

var delta = []byte{
	0x5a, 0x6d, 0x36, 0x1b, 0x0d, 0x06, 0x03, 0x41,
	0x60, 0x30, 0x18, 0x4c, 0x66, 0x33, 0x59, 0x2c,
	0x56, 0x2b, 0x15, 0x4a, 0x65, 0x72, 0x39, 0x1c,
	0x4e, 0x67, 0x73, 0x79, 0x3c, 0x5e, 0x6f, 0x37,
	0x5b, 0x2d, 0x16, 0x0b, 0x05, 0x42, 0x21, 0x50,
	0x28, 0x54, 0x2a, 0x55, 0x6a, 0x75, 0x7a, 0x7d,
	0x3e, 0x5f, 0x2f, 0x17, 0x4b, 0x25, 0x52, 0x29,
	0x14, 0x0a, 0x45, 0x62, 0x31, 0x58, 0x6c, 0x76,
	0x3b, 0x1d, 0x0e, 0x47, 0x63, 0x71, 0x78, 0x7c,
	0x7e, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x43, 0x61,
	0x70, 0x38, 0x5c, 0x6e, 0x77, 0x7b, 0x3d, 0x1e,
	0x4f, 0x27, 0x53, 0x69, 0x34, 0x1a, 0x4d, 0x26,
	0x13, 0x49, 0x24, 0x12, 0x09, 0x04, 0x02, 0x01,
	0x40, 0x20, 0x10, 0x08, 0x44, 0x22, 0x11, 0x48,
	0x64, 0x32, 0x19, 0x0c, 0x46, 0x23, 0x51, 0x68,
	0x74, 0x3a, 0x5d, 0x2e, 0x57, 0x6b, 0x35, 0x5a,
}

func subkey(mk, sk []byte) {

	for i := 0; i < 8; i++ {

		for j := 0; j < 8; j++ {
			k := j - i
			if k < 0 {
				k += 8
			}
			sk[16*i+j] = mk[16-k-1] + delta[16*i+j]
		}

		for j := 0; j < 8; j++ {
			k := j - i
			if k < 0 {
				k += 8
			}
			sk[16*i+j+8] = mk[16-k-8-1] + delta[16*i+j+8]
		}

	}
}
<commit_msg>wk and sk belong to receiver<commit_after>package dkrcrypt

// The HIGHT Block cipher from KISA
// Copyright (c) 2012 Damian Gryski <damian@gryski.com>
// Licensed under the GPLv3 or, at your option, any later version.

/*

References:

http://tools.ietf.org/html/draft-kisa-hight-00
http://www.iacr.org/cryptodb/archive/2006/CHES/04/04.pdf
http://seed.kisa.or.kr/kor/hight/hightInfo.jsp

*/

import (
	"strconv"
)

// A HightCipher is an instance of HIGHT encryption using a particular key.
type HightCipher struct {
	wk [8]byte   // whitened keys
	sk [128]byte // subkeys
}

type KeySizeError int

func (k KeySizeError) Error() string {
	return "dkrcrypt/hight: invalid key size " + strconv.Itoa(int(k))
}

// NewHight creates and returns a new HightCipher.
// The key argument should be 16 bytes.
func NewHight(key []byte) (*HightCipher, error) {
	c := new(HightCipher)

	if klen := len(key); klen != 16 {
		return nil, KeySizeError(len(key))
	}

	whiten(key, c.wk[:])
	subkey(key, c.sk[:])
	return c, nil
}

// Reset zeros the key data so that it will no longer appear in the process' memory.
func (c *HightCipher) Reset() {
	for i := range c.wk {
		c.wk[i] = 0
	}

	for i := range c.sk {
		c.sk[i] = 0
	}

}

// BlockSize returns the Hight block size.  It is needed to satisfy the Block interface in crypto/cipher.
func (c *HightCipher) BlockSize() int { return 8 }

// rotate left
func rotl(x byte, r byte) byte {
	return (x << r) | (x >> (8 - r))
}

// F0, from the specification
func f0(x byte) byte {
	return rotl(x, 1) ^ rotl(x, 2) ^ rotl(x, 7)
}

// F1, from the specification
func f1(x byte) byte {
	return rotl(x, 3) ^ rotl(x, 4) ^ rotl(x, 6)
}

// Encrypt encrypts the 8-byte block in src and stores the resulting ciphertext in dst.
func (c *HightCipher) Encrypt(dst, src []byte) {

	// numbering looks off here, because the plaintext is stored msb, but
	// having lsb makes our life easier
	x := [...]byte{
		src[7] + c.wk[0], // p0
		src[6],           // p1
		src[5] ^ c.wk[1], // p2
		src[4],           // p3
		src[3] + c.wk[2], // p4
		src[2],           // p5
		src[1] ^ c.wk[3], // p6
		src[0],           // p0
	}

	for i := 0; i < 31; i++ {
		x00 := x[7] ^ (f0(x[6]) + c.sk[4*i+3])
		x[7] = x[6]
		x[6] = x[5] + (f1(x[4]) ^ c.sk[4*i+2])
		x[5] = x[4]
		x[4] = x[3] ^ (f0(x[2]) + c.sk[4*i+1])
		x[3] = x[2]
		x[2] = x[1] + (f1(x[0]) ^ c.sk[4*i+0])
		x[1] = x[0]
		x[0] = x00
	}

	// last round
	dst[6] = x[1] + (f1(x[0]) ^ c.sk[124])
	dst[4] = x[3] ^ (f0(x[2]) + c.sk[125])
	dst[2] = x[5] + (f1(x[4]) ^ c.sk[126])
	dst[0] = x[7] ^ (f0(x[6]) + c.sk[127])

	// whitening
	dst[7] = x[0] + c.wk[4]
	dst[5] = x[2] ^ c.wk[5]
	dst[3] = x[4] + c.wk[6]
	dst[1] = x[6] ^ c.wk[7]
}

// Decrypt decrypts the 8-byte block in src and stores the resulting plaintext in dst.
func (c *HightCipher) Decrypt(dst, src []byte) {

	// whitening
	x := [...]byte{
		src[7] - c.wk[4], // c0
		src[6],           // c1
		src[5] ^ c.wk[5], // c2
		src[4],           // c3
		src[3] - c.wk[6], // c4
		src[2],           // c5
		src[1] ^ c.wk[7], // c6
		src[0],           // c7
	}

	// undo last round
	x[1] = x[1] - (f1(x[0]) ^ c.sk[124])
	x[3] = x[3] ^ (f0(x[2]) + c.sk[125])
	x[5] = x[5] - (f1(x[4]) ^ c.sk[126])
	x[7] = x[7] ^ (f0(x[6]) + c.sk[127])

	for i := 30; i >= 0; i-- {
		x00 := x[0]
		x[0] = x[1]
		x[1] = x[2] - (f1(x[1]) ^ c.sk[4*i+0])
		x[2] = x[3]
		x[3] = x[4] ^ (f0(x[3]) + c.sk[4*i+1])
		x[4] = x[5]
		x[5] = x[6] - (f1(x[5]) ^ c.sk[4*i+2])
		x[6] = x[7]
		x[7] = x00 ^ (f0(x[7]) + c.sk[4*i+3])
	}

	// undo initial whitening
	dst[7] = x[0] - c.wk[0] // p0
	dst[6] = x[1]           // p1
	dst[5] = x[2] ^ c.wk[1] // p2
	dst[4] = x[3]           // p3
	dst[3] = x[4] - c.wk[2] // p4
	dst[2] = x[5]           // p5
	dst[1] = x[6] ^ c.wk[3] // p6
	dst[0] = x[7]           // p7
}

func whiten(mk, wk []byte) {

	for i := 0; i < 4; i++ {
		wk[i] = mk[16-i-12-1]
	}

	for i := 4; i < 8; i++ {
		wk[i] = mk[16-i+4-1]
	}
}

// This table doesn't change, so rather that recompute it every time we need to
// compute subkeys, we just build it once.  The code to create it was:

/*
   s = 0x5A
   d[0] = s
   for i := 1; i < 128; i++ {
           s = (s >> 1) | (((s & 1) ^ ((s & (1 << 3)) >> 3)) << 6)
           d[i] = s
   }
*/

var delta = []byte{
	0x5a, 0x6d, 0x36, 0x1b, 0x0d, 0x06, 0x03, 0x41,
	0x60, 0x30, 0x18, 0x4c, 0x66, 0x33, 0x59, 0x2c,
	0x56, 0x2b, 0x15, 0x4a, 0x65, 0x72, 0x39, 0x1c,
	0x4e, 0x67, 0x73, 0x79, 0x3c, 0x5e, 0x6f, 0x37,
	0x5b, 0x2d, 0x16, 0x0b, 0x05, 0x42, 0x21, 0x50,
	0x28, 0x54, 0x2a, 0x55, 0x6a, 0x75, 0x7a, 0x7d,
	0x3e, 0x5f, 0x2f, 0x17, 0x4b, 0x25, 0x52, 0x29,
	0x14, 0x0a, 0x45, 0x62, 0x31, 0x58, 0x6c, 0x76,
	0x3b, 0x1d, 0x0e, 0x47, 0x63, 0x71, 0x78, 0x7c,
	0x7e, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x43, 0x61,
	0x70, 0x38, 0x5c, 0x6e, 0x77, 0x7b, 0x3d, 0x1e,
	0x4f, 0x27, 0x53, 0x69, 0x34, 0x1a, 0x4d, 0x26,
	0x13, 0x49, 0x24, 0x12, 0x09, 0x04, 0x02, 0x01,
	0x40, 0x20, 0x10, 0x08, 0x44, 0x22, 0x11, 0x48,
	0x64, 0x32, 0x19, 0x0c, 0x46, 0x23, 0x51, 0x68,
	0x74, 0x3a, 0x5d, 0x2e, 0x57, 0x6b, 0x35, 0x5a,
}

func subkey(mk, sk []byte) {

	for i := 0; i < 8; i++ {

		for j := 0; j < 8; j++ {
			k := j - i
			if k < 0 {
				k += 8
			}
			sk[16*i+j] = mk[16-k-1] + delta[16*i+j]
		}

		for j := 0; j < 8; j++ {
			k := j - i
			if k < 0 {
				k += 8
			}
			sk[16*i+j+8] = mk[16-k-8-1] + delta[16*i+j+8]
		}

	}
}
<|endoftext|>


<commit_before>package yiigo

import (
	"reflect"
	"testing"

	_ "github.com/go-sql-driver/mysql"
)

func TestInsertSQL(t *testing.T) {
	type args struct {
		table string
		data  interface{}
	}
	type Person struct {
		ID   int    `db:"id"`
		Name string `db:"name"`
		Age  int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				table: "person",
				data: &Person{
					ID:   1,
					Name: "IIInsomnia",
					Age:  29,
				},
			},
			want:  "INSERT INTO `person` (`id`, `name`, `age`) VALUES (?, ?, ?)",
			want1: []interface{}{1, "IIInsomnia", 29},
		},
		{
			name: "t2",
			args: args{
				table: "person",
				data: []*Person{
					{
						ID:   1,
						Name: "IIInsomnia",
						Age:  29,
					},
					{
						ID:   2,
						Name: "test",
						Age:  20,
					},
				},
			},
			want:  "INSERT INTO `person` (`id`, `name`, `age`) VALUES (?, ?, ?), (?, ?, ?)",
			want1: []interface{}{1, "IIInsomnia", 29, 2, "test", 20},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := InsertSQL(tt.args.table, tt.args.data)
			if got != tt.want {
				t.Errorf("InsertSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("InsertSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestUpdateSQL(t *testing.T) {
	type args struct {
		query string
		data  interface{}
		args  []interface{}
	}
	type Person struct {
		Name string `db:"name"`
		Age  int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				query: "UPDATE `person` SET ? WHERE `id` = ?",
				data: &Person{
					Name: "IIInsomnia",
					Age:  29,
				},
				args: []interface{}{1},
			},
			want:  "UPDATE `person` SET `name` = ?, `age` = ? WHERE `id` = ?",
			want1: []interface{}{"IIInsomnia", 29, 1},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := UpdateSQL(tt.args.query, tt.args.data, tt.args.args...)
			if got != tt.want {
				t.Errorf("UpdateSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("UpdateSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestPGInsertSQL(t *testing.T) {
	type args struct {
		table string
		data  interface{}
	}
	type Person struct {
		ID   int    `db:"id"`
		Name string `db:"name"`
		Age  int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				table: "person",
				data: &Person{
					ID:   1,
					Name: "IIInsomnia",
					Age:  29,
				},
			},
			want:  `INSERT INTO "person" ("id", "name", "age") VALUES ($1, $2, $3) RETURNING "id"`,
			want1: []interface{}{1, "IIInsomnia", 29},
		},
		{
			name: "t2",
			args: args{
				table: "person",
				data: []*Person{
					{
						ID:   1,
						Name: "IIInsomnia",
						Age:  29,
					},
					{
						ID:   2,
						Name: "test",
						Age:  20,
					},
				},
			},
			want:  `INSERT INTO "person" ("id", "name", "age") VALUES ($1, $2, $3), ($4, $5, $6)`,
			want1: []interface{}{1, "IIInsomnia", 29, 2, "test", 20},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := PGInsertSQL(tt.args.table, tt.args.data)
			if got != tt.want {
				t.Errorf("PGInsertSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("PGInsertSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestPGUpdateSQL(t *testing.T) {
	type args struct {
		query string
		data  interface{}
		args  []interface{}
	}
	type Person struct {
		Name string `db:"name"`
		Age  int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				query: `UPDATE "person" SET $1 WHERE "id" = $2`,
				data: &Person{
					Name: "IIInsomnia",
					Age:  29,
				},
				args: []interface{}{1},
			},
			want:  `UPDATE "person" SET "name" = $1, "age" = $2 WHERE "id" = $3`,
			want1: []interface{}{"IIInsomnia", 29, 1},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := PGUpdateSQL(tt.args.query, tt.args.data, tt.args.args...)
			if got != tt.want {
				t.Errorf("PGUpdateSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("PGUpdateSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}
<commit_msg>improve db opt<commit_after>package yiigo

import (
	"reflect"
	"testing"

	_ "github.com/go-sql-driver/mysql"
)

func TestInsertSQL(t *testing.T) {
	type args struct {
		table string
		data  interface{}
	}
	type Person struct {
		ID     int    `db:"id"`
		Name   string `db:"name"`
		Gender string `db:"gender"`
		Age    int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				table: "person",
				data: &Person{
					ID:     1,
					Name:   "IIInsomnia",
					Gender: "M",
					Age:    29,
				},
			},
			want:  "INSERT INTO `person` (`id`, `name`, `gender`, `age`) VALUES (?, ?, ?, ?)",
			want1: []interface{}{1, "IIInsomnia", "M", 29},
		},
		{
			name: "t2",
			args: args{
				table: "person",
				data: []*Person{
					{
						ID:     1,
						Name:   "IIInsomnia",
						Gender: "M",
						Age:    29,
					},
					{
						ID:     2,
						Name:   "test",
						Gender: "W",
						Age:    20,
					},
				},
			},
			want:  "INSERT INTO `person` (`id`, `name`, `gender`, `age`) VALUES (?, ?, ?, ?), (?, ?, ?, ?)",
			want1: []interface{}{1, "IIInsomnia", "M", 29, 2, "test", "W", 20},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := InsertSQL(tt.args.table, tt.args.data)
			if got != tt.want {
				t.Errorf("InsertSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("InsertSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestUpdateSQL(t *testing.T) {
	type args struct {
		query string
		data  interface{}
		args  []interface{}
	}
	type Person struct {
		Name   string `db:"name"`
		Gender string `db:"gender"`
		Age    int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				query: "UPDATE `person` SET ? WHERE `id` = ?",
				data: &Person{
					Name:   "IIInsomnia",
					Gender: "M",
					Age:    29,
				},
				args: []interface{}{1},
			},
			want:  "UPDATE `person` SET `name` = ?, `gender` = ?, `age` = ? WHERE `id` = ?",
			want1: []interface{}{"IIInsomnia", "M", 29, 1},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := UpdateSQL(tt.args.query, tt.args.data, tt.args.args...)
			if got != tt.want {
				t.Errorf("UpdateSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("UpdateSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestPGInsertSQL(t *testing.T) {
	type args struct {
		table string
		data  interface{}
	}
	type Person struct {
		ID     int    `db:"id"`
		Name   string `db:"name"`
		Gender string `db:"gender"`
		Age    int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				table: "person",
				data: &Person{
					ID:     1,
					Name:   "IIInsomnia",
					Gender: "M",
					Age:    29,
				},
			},
			want:  `INSERT INTO "person" ("id", "name", "gender", "age") VALUES ($1, $2, $3, $4) RETURNING "id"`,
			want1: []interface{}{1, "IIInsomnia", "M", 29},
		},
		{
			name: "t2",
			args: args{
				table: "person",
				data: []*Person{
					{
						ID:     1,
						Name:   "IIInsomnia",
						Gender: "M",
						Age:    29,
					},
					{
						ID:     2,
						Name:   "test",
						Gender: "W",
						Age:    20,
					},
				},
			},
			want:  `INSERT INTO "person" ("id", "name", "gender", "age") VALUES ($1, $2, $3, $4), ($5, $6, $7, $8)`,
			want1: []interface{}{1, "IIInsomnia", "M", 29, 2, "test", "W", 20},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := PGInsertSQL(tt.args.table, tt.args.data)
			if got != tt.want {
				t.Errorf("PGInsertSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("PGInsertSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestPGUpdateSQL(t *testing.T) {
	type args struct {
		query string
		data  interface{}
		args  []interface{}
	}
	type Person struct {
		Name   string `db:"name"`
		Gender string `db:"gender"`
		Age    int    `db:"age"`
	}
	tests := []struct {
		name  string
		args  args
		want  string
		want1 []interface{}
	}{
		{
			name: "t1",
			args: args{
				query: `UPDATE "person" SET $1 WHERE "id" = $2`,
				data: &Person{
					Name:   "IIInsomnia",
					Gender: "M",
					Age:    29,
				},
				args: []interface{}{1},
			},
			want:  `UPDATE "person" SET "name" = $1, "gender" = $2, "age" = $3 WHERE "id" = $4`,
			want1: []interface{}{"IIInsomnia", "M", 29, 1},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := PGUpdateSQL(tt.args.query, tt.args.data, tt.args.args...)
			if got != tt.want {
				t.Errorf("PGUpdateSQL() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("PGUpdateSQL() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}
<|endoftext|>


<commit_before>package ebpf

import (
	"bytes"
	"fmt"
	"io"
	"syscall"
	"testing"
)

func TestPerfReader(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		Watermark:    1,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	ret, _, err := prog.Test(make([]byte, 14))
	if err != nil {
		t.Fatal(err)
	}

	if errno := syscall.Errno(-int32(ret)); errno != 0 {
		t.Fatal("Expected 0 as return value, got", errno)
	}

	sample := <-rd.Samples
	want := []byte{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0}
	if !bytes.Equal(sample.Data, want) {
		t.Log(sample.Data)
		t.Error("Sample doesn't match expected output")
	}
}

func TestPerfReaderLostSample(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		// This is chosen to notify _after_ the lost sample record
		// has been created.
		Watermark: 4032,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("create_lost_sample")
	defer prog.Close()

	ret, _, err := prog.Test(make([]byte, 14))
	if err != nil {
		t.Fatal(err)
	}

	if errno := syscall.Errno(-int32(ret)); errno != 0 {
		t.Fatal("Expected 0 as return value, got", errno)
	}

	for sample := range rd.Samples {
		// Wait for small sample, as an indicator that the reader has processed
		// the lost event.
		if len(sample.Data) == 4 {
			break
		}
	}

	if lost := rd.LostSamples(); lost != 1 {
		t.Error("Expected 1 lost sample, got", lost)
	}
}

func TestPerfReaderClose(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		Watermark:    1,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	// more samples than the channel capacity
	for i := 0; i < cap(rd.Samples)*2; i++ {
		ret, _, err := prog.Test(make([]byte, 14))
		if err != nil {
			t.Fatal(err)
		}

		if errno := syscall.Errno(-int32(ret)); errno != 0 {
			t.Fatal("Expected 0 as return value, got", errno)
		}
	}

	// Close shouldn't block on us not reading
	rd.Close()

	// And we should be able to call it multiple times
	rd.Close()
}

func TestPerfReaderFlushAndClose(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		Watermark:    1,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	// more samples than the channel capacity
	numSamples := cap(rd.Samples)*2
	for i := 0; i < numSamples; i++ {
		ret, _, err := prog.Test(make([]byte, 14))
		if err != nil {
			t.Fatal(err)
		}

		if errno := syscall.Errno(-int32(ret)); errno != 0 {
			t.Fatal("Expected 0 as return value, got", errno)
		}
	}

	done := make(chan struct{})
	go func(){
		rd.FlushAndClose()
		// Should be able to call this multiple times
		rd.FlushAndClose()
		close(done)
	}()

	received := 0
	for _ = range rd.Samples {
		received++
	}

	if received != numSamples {
		t.Fatalf("Expected %d samples got %d", numSamples, received)
	}

	<-done
}

func TestRingBuffer(t *testing.T) {
	buf := make([]byte, 2)

	ring := makeRing(2, 0)
	n, err := ring.Read(buf)
	if err != io.EOF {
		t.Error("Expected io.EOF, got", err)
	}
	if n != 2 {
		t.Errorf("Expected to read 2 bytes, got %d", n)
	}
	if !bytes.Equal(buf, []byte{0, 1}) {
		t.Error("Expected [0, 1], got", buf)
	}
	n, err = ring.Read(buf)
	if err != io.EOF {
		t.Error("Expected io.EOF, got", err)
	}
	if n != 0 {
		t.Error("Expected to read 0 bytes, got", n)
	}

	// Wrapping read
	ring = makeRing(2, 1)
	n, err = io.ReadFull(ring, buf)
	if err != nil {
		t.Error("Error while reading:", err)
	}
	if n != 2 {
		t.Errorf("Expected to read 2 byte, got %d", n)
	}
	if !bytes.Equal(buf, []byte{1, 0}) {
		t.Error("Expected [1, 0], got", buf)
	}
}

func makeRing(size, offset int) *ringReader {
	if size%2 != 0 {
		panic("size must be power of two")
	}

	ring := make([]byte, size)
	for i := range ring {
		ring[i] = byte(i)
	}

	meta := perfEventMeta{
		dataHead: uint64(len(ring) + offset),
		dataTail: uint64(offset),
		dataSize: uint64(len(ring)),
	}

	return newRingReader(&meta, ring)
}

// ExamplePerfReader submits a perf event using BPF,
// and then reads it in user space.
//
// The BPF will look something like this:
//
//    struct map events __section("maps") = {
//      .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
//    };
//
//    __section("xdp") int output_single(void *ctx) {
//      unsigned char buf[] = {
//        1, 2, 3, 4, 5
//      };
//
//       return perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &buf[0], 5);
//     }
//
// Also see BPF_F_CTXLEN_MASK if you want to sample packet data
// from SKB or XDP programs.
func ExamplePerfReader() {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		panic(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		// Notify immediately
		Watermark: 1,
	})
	if err != nil {
		panic(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	ret, _, err := prog.Test(make([]byte, 14))
	if err != nil {
		panic(err)
	}

	if ret != 0 {
		panic("expected 0 return value")
	}

	select {
	case sample := <-rd.Samples:
		// Data is padded with 0 for alignment
		fmt.Println("Sample:", sample.Data)
	case err := <-rd.Error:
		panic(err)
	}

	// Output: Sample: [1 2 3 4 5 0 0 0 0 0 0 0]
}
<commit_msg>gofmt perf_test<commit_after>package ebpf

import (
	"bytes"
	"fmt"
	"io"
	"syscall"
	"testing"
)

func TestPerfReader(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		Watermark:    1,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	ret, _, err := prog.Test(make([]byte, 14))
	if err != nil {
		t.Fatal(err)
	}

	if errno := syscall.Errno(-int32(ret)); errno != 0 {
		t.Fatal("Expected 0 as return value, got", errno)
	}

	sample := <-rd.Samples
	want := []byte{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0}
	if !bytes.Equal(sample.Data, want) {
		t.Log(sample.Data)
		t.Error("Sample doesn't match expected output")
	}
}

func TestPerfReaderLostSample(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		// This is chosen to notify _after_ the lost sample record
		// has been created.
		Watermark: 4032,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("create_lost_sample")
	defer prog.Close()

	ret, _, err := prog.Test(make([]byte, 14))
	if err != nil {
		t.Fatal(err)
	}

	if errno := syscall.Errno(-int32(ret)); errno != 0 {
		t.Fatal("Expected 0 as return value, got", errno)
	}

	for sample := range rd.Samples {
		// Wait for small sample, as an indicator that the reader has processed
		// the lost event.
		if len(sample.Data) == 4 {
			break
		}
	}

	if lost := rd.LostSamples(); lost != 1 {
		t.Error("Expected 1 lost sample, got", lost)
	}
}

func TestPerfReaderClose(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		Watermark:    1,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	// more samples than the channel capacity
	for i := 0; i < cap(rd.Samples)*2; i++ {
		ret, _, err := prog.Test(make([]byte, 14))
		if err != nil {
			t.Fatal(err)
		}

		if errno := syscall.Errno(-int32(ret)); errno != 0 {
			t.Fatal("Expected 0 as return value, got", errno)
		}
	}

	// Close shouldn't block on us not reading
	rd.Close()

	// And we should be able to call it multiple times
	rd.Close()
}

func TestPerfReaderFlushAndClose(t *testing.T) {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		t.Fatal(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		Watermark:    1,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	// more samples than the channel capacity
	numSamples := cap(rd.Samples) * 2
	for i := 0; i < numSamples; i++ {
		ret, _, err := prog.Test(make([]byte, 14))
		if err != nil {
			t.Fatal(err)
		}

		if errno := syscall.Errno(-int32(ret)); errno != 0 {
			t.Fatal("Expected 0 as return value, got", errno)
		}
	}

	done := make(chan struct{})
	go func() {
		rd.FlushAndClose()
		// Should be able to call this multiple times
		rd.FlushAndClose()
		close(done)
	}()

	received := 0
	for _ = range rd.Samples {
		received++
	}

	if received != numSamples {
		t.Fatalf("Expected %d samples got %d", numSamples, received)
	}

	<-done
}

func TestRingBuffer(t *testing.T) {
	buf := make([]byte, 2)

	ring := makeRing(2, 0)
	n, err := ring.Read(buf)
	if err != io.EOF {
		t.Error("Expected io.EOF, got", err)
	}
	if n != 2 {
		t.Errorf("Expected to read 2 bytes, got %d", n)
	}
	if !bytes.Equal(buf, []byte{0, 1}) {
		t.Error("Expected [0, 1], got", buf)
	}
	n, err = ring.Read(buf)
	if err != io.EOF {
		t.Error("Expected io.EOF, got", err)
	}
	if n != 0 {
		t.Error("Expected to read 0 bytes, got", n)
	}

	// Wrapping read
	ring = makeRing(2, 1)
	n, err = io.ReadFull(ring, buf)
	if err != nil {
		t.Error("Error while reading:", err)
	}
	if n != 2 {
		t.Errorf("Expected to read 2 byte, got %d", n)
	}
	if !bytes.Equal(buf, []byte{1, 0}) {
		t.Error("Expected [1, 0], got", buf)
	}
}

func makeRing(size, offset int) *ringReader {
	if size%2 != 0 {
		panic("size must be power of two")
	}

	ring := make([]byte, size)
	for i := range ring {
		ring[i] = byte(i)
	}

	meta := perfEventMeta{
		dataHead: uint64(len(ring) + offset),
		dataTail: uint64(offset),
		dataSize: uint64(len(ring)),
	}

	return newRingReader(&meta, ring)
}

// ExamplePerfReader submits a perf event using BPF,
// and then reads it in user space.
//
// The BPF will look something like this:
//
//    struct map events __section("maps") = {
//      .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
//    };
//
//    __section("xdp") int output_single(void *ctx) {
//      unsigned char buf[] = {
//        1, 2, 3, 4, 5
//      };
//
//       return perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &buf[0], 5);
//     }
//
// Also see BPF_F_CTXLEN_MASK if you want to sample packet data
// from SKB or XDP programs.
func ExamplePerfReader() {
	coll, err := LoadCollection("testdata/perf_output.elf")
	if err != nil {
		panic(err)
	}
	defer coll.Close()

	rd, err := NewPerfReader(PerfReaderOptions{
		Map:          coll.DetachMap("events"),
		PerCPUBuffer: 4096,
		// Notify immediately
		Watermark: 1,
	})
	if err != nil {
		panic(err)
	}
	defer rd.Close()

	prog := coll.DetachProgram("output_single")
	defer prog.Close()

	ret, _, err := prog.Test(make([]byte, 14))
	if err != nil {
		panic(err)
	}

	if ret != 0 {
		panic("expected 0 return value")
	}

	select {
	case sample := <-rd.Samples:
		// Data is padded with 0 for alignment
		fmt.Println("Sample:", sample.Data)
	case err := <-rd.Error:
		panic(err)
	}

	// Output: Sample: [1 2 3 4 5 0 0 0 0 0 0 0]
}
<|endoftext|>


<commit_before>package perl

/*
#cgo CFLAGS: -D_THREAD_SAFE -pthread -I../vendor/perl-5.20.1 -I/usr/local/include
#cgo LDFLAGS: -fstack-protector -L/usr/local/lib -L$GOPATH/github.com/ian-kent/purl/vendor/perl-5.20.1 -lperl -ldl -lm -lutil -lc -fno-common -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include
#include "c/purl.h"
*/
import "C"
import (
	"sync"
	"unsafe"
)

var perlMutex sync.Mutex
var xsMap = make(map[string]*func(...string) string)

// Purl is a Perl interpreter instance.
//
// You must call Init before calling any other functions.
// You must call Destroy when finished with the instance.
type Purl struct {
	init    bool
	destroy bool
}

// Init initialises the Perl interpreter instance
func (p *Purl) Init() {
	perlMutex.Lock()
	if !p.init {
		C.PurlInit()
		p.init = true
		p.destroy = false
	}
	perlMutex.Unlock()
}

// Destroy destroys the Perl interpreter instance
func (p *Purl) Destroy() {
	perlMutex.Lock()
	if !p.destroy {
		C.PurlDestroy()
		p.destroy = true
		p.init = false
	}
	perlMutex.Unlock()
}

// RegisterXS makes a Go function available in Perl
// via an XS callback.
//
// It currently only supports a variadic string input
// and a single scalar string output.
func (p *Purl) RegisterXS(name string, f func(...string) string) {
	xsMap[name] = &f
	p.Eval(`
package main {
	*{"` + name + `"} = sub { Purl::XS->Invoke("` + name + `") };
}
`)
}

// Eval evaluates Perl code.
//
// Interpreter state is persisted until Destroy is called.
func (p *Purl) Eval(src string) string {
	csrc := C.CString(src)
	defer C.free(unsafe.Pointer(csrc))

	cres := C.EvalPerl(csrc)
	if cres == nil {
		return ""
	}

	// Not sure if this is required? causes memory issues
	//defer C.free(unsafe.Pointer(cres))
	return C.GoString(cres)
}

func newString(s string) *C.SV {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	str := C.Perl_newSVpvn(cs, C.STRLEN(len(s)))
	return str
}
<commit_msg>Try again<commit_after>package perl

/*
#cgo CFLAGS: -D_THREAD_SAFE -pthread -I../vendor/perl-5.20.1 -L/usr/local/lib -I/usr/local/include
#cgo LDFLAGS: -fstack-protector -L/usr/local/lib -L$GOPATH/github.com/ian-kent/purl/vendor/perl-5.20.1 -lperl -ldl -lm -lutil -lc -fno-common -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include
#include "c/purl.h"
*/
import "C"
import (
	"sync"
	"unsafe"
)

var perlMutex sync.Mutex
var xsMap = make(map[string]*func(...string) string)

// Purl is a Perl interpreter instance.
//
// You must call Init before calling any other functions.
// You must call Destroy when finished with the instance.
type Purl struct {
	init    bool
	destroy bool
}

// Init initialises the Perl interpreter instance
func (p *Purl) Init() {
	perlMutex.Lock()
	if !p.init {
		C.PurlInit()
		p.init = true
		p.destroy = false
	}
	perlMutex.Unlock()
}

// Destroy destroys the Perl interpreter instance
func (p *Purl) Destroy() {
	perlMutex.Lock()
	if !p.destroy {
		C.PurlDestroy()
		p.destroy = true
		p.init = false
	}
	perlMutex.Unlock()
}

// RegisterXS makes a Go function available in Perl
// via an XS callback.
//
// It currently only supports a variadic string input
// and a single scalar string output.
func (p *Purl) RegisterXS(name string, f func(...string) string) {
	xsMap[name] = &f
	p.Eval(`
package main {
	*{"` + name + `"} = sub { Purl::XS->Invoke("` + name + `") };
}
`)
}

// Eval evaluates Perl code.
//
// Interpreter state is persisted until Destroy is called.
func (p *Purl) Eval(src string) string {
	csrc := C.CString(src)
	defer C.free(unsafe.Pointer(csrc))

	cres := C.EvalPerl(csrc)
	if cres == nil {
		return ""
	}

	// Not sure if this is required? causes memory issues
	//defer C.free(unsafe.Pointer(cres))
	return C.GoString(cres)
}

func newString(s string) *C.SV {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	str := C.Perl_newSVpvn(cs, C.STRLEN(len(s)))
	return str
}
<|endoftext|>


<commit_before>package yext

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	HoursClosedAllWeek = "1:closed,2:closed,3:closed,4:closed,5:closed,6:closed,7:closed"
	HoursOpen24Hours   = "00:00:00:00"
	HoursClosed        = "closed"
)

type Weekday int

// Following the documentation of the Yext API,
// the indexing of days begins at 1 (with Sunday)
// and ends with 7 (Saturday)
// http://developer.yext.com/docs/api-reference/
const (
	Sunday Weekday = iota + 1
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
)

func (w Weekday) ToString() string {
	switch w {
	case Sunday:
		return "Sunday"
	case Monday:
		return "Monday"
	case Tuesday:
		return "Tuesday"
	case Wednesday:
		return "Wednesday"
	case Thursday:
		return "Thursday"
	case Friday:
		return "Friday"
	case Saturday:
		return "Saturday"
	}
	return "Unknown"
}

type HoursHelper struct {
	Sunday    []string
	Monday    []string
	Tuesday   []string
	Wednesday []string
	Thursday  []string
	Friday    []string
	Saturday  []string
}

func HoursHelperFromString(str string) (*HoursHelper, error) {
	var (
		hoursHelper  = &HoursHelper{}
		hoursForDays = strings.Split(str, ",")
	)
	if len(str) == 0 {
		return hoursHelper, nil
	}
	for _, hoursForDay := range hoursForDays {
		weekday, hours, err := parseWeekdayAndHoursFromString(hoursForDay)
		if err != nil {
			return nil, err
		}
		hoursHelper.AppendHours(weekday, hours)
	}
	for weekday, hours := range hoursHelper.ToMap() {
		if hours == nil {
			hoursHelper.SetClosed(weekday)
		}
	}
	return hoursHelper, nil
}

func MustHoursHelperFromString(str string) *HoursHelper {
	hoursHelper, err := HoursHelperFromString(str)
	if err != nil {
		panic(err)
	}
	return hoursHelper
}

func (h *HoursHelper) SetHours(weekday Weekday, hours []string) {
	switch weekday {
	case Sunday:
		h.Sunday = hours
	case Monday:
		h.Monday = hours
	case Tuesday:
		h.Tuesday = hours
	case Wednesday:
		h.Wednesday = hours
	case Thursday:
		h.Thursday = hours
	case Friday:
		h.Friday = hours
	case Saturday:
		h.Saturday = hours
	}
}

func (h *HoursHelper) AppendHours(weekday Weekday, hours string) {
	switch weekday {
	case Sunday:
		h.Sunday = append(h.Sunday, hours)
	case Monday:
		h.Monday = append(h.Monday, hours)
	case Tuesday:
		h.Tuesday = append(h.Tuesday, hours)
	case Wednesday:
		h.Wednesday = append(h.Wednesday, hours)
	case Thursday:
		h.Thursday = append(h.Thursday, hours)
	case Friday:
		h.Friday = append(h.Friday, hours)
	case Saturday:
		h.Saturday = append(h.Saturday, hours)
	}
}

func (h *HoursHelper) SetClosed(weekday Weekday) {
	h.SetHours(weekday, []string{HoursClosed})
}

func (h *HoursHelper) SetUnspecified(weekday Weekday) {
	h.SetHours(weekday, nil)
}

func (h *HoursHelper) GetHours(weekday Weekday) []string {
	switch weekday {
	case Sunday:
		return h.Sunday
	case Monday:
		return h.Monday
	case Tuesday:
		return h.Tuesday
	case Wednesday:
		return h.Wednesday
	case Thursday:
		return h.Thursday
	case Friday:
		return h.Friday
	case Saturday:
		return h.Saturday
	}
	return nil
}

func (h *HoursHelper) Serialize() string {
	if h.HoursAreAllUnspecified() {
		return ""
	}
	var days = []string{
		h.SerializeDay(Sunday),
		h.SerializeDay(Monday),
		h.SerializeDay(Tuesday),
		h.SerializeDay(Wednesday),
		h.SerializeDay(Thursday),
		h.SerializeDay(Friday),
		h.SerializeDay(Saturday),
	}
	return strings.Join(days, ",")
}

func (h *HoursHelper) SerializeDay(weekday Weekday) string {
	if h.HoursAreAllUnspecified() {
		return ""
	}
	var hoursStrings = []string{}
	if h.GetHours(weekday) == nil || len(h.GetHours(weekday)) == 0 {
		return fmt.Sprintf("%d:%s", weekday, HoursClosed)
	}
	for _, hours := range h.GetHours(weekday) {
		hoursStrings = append(hoursStrings, fmt.Sprintf("%d:%s", weekday, hours))
	}
	return strings.Join(hoursStrings, ",")
}

func (h *HoursHelper) ToStringSlice() ([]string, error) {
	var hoursStringSlice = make([][]string, 7)
	for i := range hoursStringSlice {
		weekday := Weekday(i + 1)
		if h.HoursAreClosed(weekday) {
			hoursStringSlice[i] = []string{"Closed"}
		} else if h.HoursAreOpen24Hours(weekday) {
			hoursStringSlice[i] = []string{"24hr"}
		} else {
			hours := h.GetHours(weekday)
			for _, h := range hours {
				open, close, err := ParseOpenAndCloseHoursFromString(h)
				if err != nil {
					return nil, err
				}
				if open, err = ConvertBetweenFormats(open, "15:04", "3:04pm"); err != nil {
					return nil, err
				}
				if close, err = ConvertBetweenFormats(close, "15:04", "3:04pm"); err != nil {
					return nil, err
				}
				hoursStringSlice[i] = append(hoursStringSlice[i], fmt.Sprintf("%s - %s", open, close))
			}
		}
	}
	var hoursSlice = make([]string, 7)
	for i, h := range hoursStringSlice {
		hoursSlice[i] = strings.Join(h, ",")
	}
	return hoursSlice, nil
}

func (h *HoursHelper) MustToStringSlice() []string {
	hoursStringSlice, err := h.ToStringSlice()
	if err != nil {
		panic(err)
	}
	return hoursStringSlice
}

func parseWeekdayAndHoursFromString(str string) (Weekday, string, error) {
	if len(str) == 0 {
		return -1, "", fmt.Errorf("Error parsing weekday and hours from string: string has 0 length")
	}
	hoursParts := strings.Split(str, ":")
	if len(hoursParts) == 0 {
		return -1, "", fmt.Errorf("Error parsing weekday and hours from string: string in unexpectd format")
	}
	weekdayInt, err := strconv.Atoi(hoursParts[0])
	if err != nil {
		return -1, "", fmt.Errorf("Error parsing weekday hours from string; unable to convert index to num: %s", err)
	}
	return Weekday(weekdayInt), strings.Join(hoursParts[1:], ":"), nil
}

func ParseOpenAndCloseHoursFromString(hours string) (string, string, error) {
	if strings.Contains(hours, ":") {
		parts := strings.Split(hours, ":")
		if len(parts) == 4 {
			return fmt.Sprintf("%s:%s", parts[0], parts[1]), fmt.Sprintf("%s:%s", parts[2], parts[3]), nil
		} else if len(parts) == 5 {
			return fmt.Sprintf("%s:%s", parts[1], parts[2]), fmt.Sprintf("%s:%s", parts[3], parts[4]), nil
		}
	}
	return "", "", fmt.Errorf("Error parsing open and close hours from string %s: Unexpected format", hours)
}

func (h HoursHelper) ToMap() map[Weekday][]string {
	return map[Weekday][]string{
		Sunday:    h.Sunday,
		Monday:    h.Monday,
		Tuesday:   h.Tuesday,
		Wednesday: h.Wednesday,
		Thursday:  h.Thursday,
		Friday:    h.Friday,
		Saturday:  h.Saturday,
	}
}

func (h *HoursHelper) HoursAreAllUnspecified() bool {
	for _, hours := range h.ToMap() {
		if hours != nil {
			return false
		}
	}
	return true
}

func (h *HoursHelper) HoursAreUnspecified(weekday Weekday) bool {
	return h.GetHours(weekday) == nil
}

func (h *HoursHelper) HoursAreClosed(weekday Weekday) bool {
	var hours = h.GetHours(weekday)
	return hours != nil && len(hours) == 1 && hours[0] == HoursClosed
}

func (h *HoursHelper) HoursAreOpen24Hours(weekday Weekday) bool {
	var hours = h.GetHours(weekday)
	return hours != nil && len(hours) == 1 && hours[0] == HoursOpen24Hours
}

func ParseAndFormatHours(tFormat string, openHours string, closeHours string) (string, error) {
	openTime, err := time.Parse(tFormat, openHours)
	if err != nil {
		return "", fmt.Errorf("Error parsing hours %s with format %s: %s", openHours, tFormat, err)
	}
	closeTime, err := time.Parse(tFormat, closeHours)
	if err != nil {
		return "", fmt.Errorf("Error parsing hours %s with format %s: %s", closeHours, tFormat, err)
	}
	return fmt.Sprintf("%s:%s", openTime.Format("15:04"), closeTime.Format("15:04")), nil
}

func ConvertBetweenFormats(hours string, convertFromFormat string, convertToFormat string) (string, error) {
	t, err := time.Parse(convertFromFormat, hours)
	if err != nil {
		return "", fmt.Errorf("Hours %s was not in expected format %s: %s", hours, convertFromFormat, err)
	}
	return t.Format(convertToFormat), nil
}
<commit_msg>Add SetOpen24Hours function to HoursHelper<commit_after>package yext

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	HoursClosedAllWeek = "1:closed,2:closed,3:closed,4:closed,5:closed,6:closed,7:closed"
	HoursOpen24Hours   = "00:00:00:00"
	HoursClosed        = "closed"
)

type Weekday int

// Following the documentation of the Yext API,
// the indexing of days begins at 1 (with Sunday)
// and ends with 7 (Saturday)
// http://developer.yext.com/docs/api-reference/
const (
	Sunday Weekday = iota + 1
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
)

func (w Weekday) ToString() string {
	switch w {
	case Sunday:
		return "Sunday"
	case Monday:
		return "Monday"
	case Tuesday:
		return "Tuesday"
	case Wednesday:
		return "Wednesday"
	case Thursday:
		return "Thursday"
	case Friday:
		return "Friday"
	case Saturday:
		return "Saturday"
	}
	return "Unknown"
}

type HoursHelper struct {
	Sunday    []string
	Monday    []string
	Tuesday   []string
	Wednesday []string
	Thursday  []string
	Friday    []string
	Saturday  []string
}

func HoursHelperFromString(str string) (*HoursHelper, error) {
	var (
		hoursHelper  = &HoursHelper{}
		hoursForDays = strings.Split(str, ",")
	)
	if len(str) == 0 {
		return hoursHelper, nil
	}
	for _, hoursForDay := range hoursForDays {
		weekday, hours, err := parseWeekdayAndHoursFromString(hoursForDay)
		if err != nil {
			return nil, err
		}
		hoursHelper.AppendHours(weekday, hours)
	}
	for weekday, hours := range hoursHelper.ToMap() {
		if hours == nil {
			hoursHelper.SetClosed(weekday)
		}
	}
	return hoursHelper, nil
}

func MustHoursHelperFromString(str string) *HoursHelper {
	hoursHelper, err := HoursHelperFromString(str)
	if err != nil {
		panic(err)
	}
	return hoursHelper
}

func (h *HoursHelper) SetHours(weekday Weekday, hours []string) {
	switch weekday {
	case Sunday:
		h.Sunday = hours
	case Monday:
		h.Monday = hours
	case Tuesday:
		h.Tuesday = hours
	case Wednesday:
		h.Wednesday = hours
	case Thursday:
		h.Thursday = hours
	case Friday:
		h.Friday = hours
	case Saturday:
		h.Saturday = hours
	}
}

func (h *HoursHelper) AppendHours(weekday Weekday, hours string) {
	switch weekday {
	case Sunday:
		h.Sunday = append(h.Sunday, hours)
	case Monday:
		h.Monday = append(h.Monday, hours)
	case Tuesday:
		h.Tuesday = append(h.Tuesday, hours)
	case Wednesday:
		h.Wednesday = append(h.Wednesday, hours)
	case Thursday:
		h.Thursday = append(h.Thursday, hours)
	case Friday:
		h.Friday = append(h.Friday, hours)
	case Saturday:
		h.Saturday = append(h.Saturday, hours)
	}
}

func (h *HoursHelper) SetClosed(weekday Weekday) {
	h.SetHours(weekday, []string{HoursClosed})
}

func (h *HoursHelper) SetUnspecified(weekday Weekday) {
	h.SetHours(weekday, nil)
}

func (h *HoursHelper) SetOpen24Hours(weekday Weekday) {
	h.SetHours(weekday, []string{HoursOpen24Hours})
}

func (h *HoursHelper) GetHours(weekday Weekday) []string {
	switch weekday {
	case Sunday:
		return h.Sunday
	case Monday:
		return h.Monday
	case Tuesday:
		return h.Tuesday
	case Wednesday:
		return h.Wednesday
	case Thursday:
		return h.Thursday
	case Friday:
		return h.Friday
	case Saturday:
		return h.Saturday
	}
	return nil
}

func (h *HoursHelper) Serialize() string {
	if h.HoursAreAllUnspecified() {
		return ""
	}
	var days = []string{
		h.SerializeDay(Sunday),
		h.SerializeDay(Monday),
		h.SerializeDay(Tuesday),
		h.SerializeDay(Wednesday),
		h.SerializeDay(Thursday),
		h.SerializeDay(Friday),
		h.SerializeDay(Saturday),
	}
	return strings.Join(days, ",")
}

func (h *HoursHelper) SerializeDay(weekday Weekday) string {
	if h.HoursAreAllUnspecified() {
		return ""
	}
	var hoursStrings = []string{}
	if h.GetHours(weekday) == nil || len(h.GetHours(weekday)) == 0 {
		return fmt.Sprintf("%d:%s", weekday, HoursClosed)
	}
	for _, hours := range h.GetHours(weekday) {
		hoursStrings = append(hoursStrings, fmt.Sprintf("%d:%s", weekday, hours))
	}
	return strings.Join(hoursStrings, ",")
}

func (h *HoursHelper) ToStringSlice() ([]string, error) {
	var hoursStringSlice = make([][]string, 7)
	for i := range hoursStringSlice {
		weekday := Weekday(i + 1)
		if h.HoursAreClosed(weekday) {
			hoursStringSlice[i] = []string{"Closed"}
		} else if h.HoursAreOpen24Hours(weekday) {
			hoursStringSlice[i] = []string{"24hr"}
		} else {
			hours := h.GetHours(weekday)
			for _, h := range hours {
				open, close, err := ParseOpenAndCloseHoursFromString(h)
				if err != nil {
					return nil, err
				}
				if open, err = ConvertBetweenFormats(open, "15:04", "3:04pm"); err != nil {
					return nil, err
				}
				if close, err = ConvertBetweenFormats(close, "15:04", "3:04pm"); err != nil {
					return nil, err
				}
				hoursStringSlice[i] = append(hoursStringSlice[i], fmt.Sprintf("%s - %s", open, close))
			}
		}
	}
	var hoursSlice = make([]string, 7)
	for i, h := range hoursStringSlice {
		hoursSlice[i] = strings.Join(h, ",")
	}
	return hoursSlice, nil
}

func (h *HoursHelper) MustToStringSlice() []string {
	hoursStringSlice, err := h.ToStringSlice()
	if err != nil {
		panic(err)
	}
	return hoursStringSlice
}

func parseWeekdayAndHoursFromString(str string) (Weekday, string, error) {
	if len(str) == 0 {
		return -1, "", fmt.Errorf("Error parsing weekday and hours from string: string has 0 length")
	}
	hoursParts := strings.Split(str, ":")
	if len(hoursParts) == 0 {
		return -1, "", fmt.Errorf("Error parsing weekday and hours from string: string in unexpectd format")
	}
	weekdayInt, err := strconv.Atoi(hoursParts[0])
	if err != nil {
		return -1, "", fmt.Errorf("Error parsing weekday hours from string; unable to convert index to num: %s", err)
	}
	return Weekday(weekdayInt), strings.Join(hoursParts[1:], ":"), nil
}

func ParseOpenAndCloseHoursFromString(hours string) (string, string, error) {
	if strings.Contains(hours, ":") {
		parts := strings.Split(hours, ":")
		if len(parts) == 4 {
			return fmt.Sprintf("%s:%s", parts[0], parts[1]), fmt.Sprintf("%s:%s", parts[2], parts[3]), nil
		} else if len(parts) == 5 {
			return fmt.Sprintf("%s:%s", parts[1], parts[2]), fmt.Sprintf("%s:%s", parts[3], parts[4]), nil
		}
	}
	return "", "", fmt.Errorf("Error parsing open and close hours from string %s: Unexpected format", hours)
}

func (h HoursHelper) ToMap() map[Weekday][]string {
	return map[Weekday][]string{
		Sunday:    h.Sunday,
		Monday:    h.Monday,
		Tuesday:   h.Tuesday,
		Wednesday: h.Wednesday,
		Thursday:  h.Thursday,
		Friday:    h.Friday,
		Saturday:  h.Saturday,
	}
}

func (h *HoursHelper) HoursAreAllUnspecified() bool {
	for _, hours := range h.ToMap() {
		if hours != nil {
			return false
		}
	}
	return true
}

func (h *HoursHelper) HoursAreUnspecified(weekday Weekday) bool {
	return h.GetHours(weekday) == nil
}

func (h *HoursHelper) HoursAreClosed(weekday Weekday) bool {
	var hours = h.GetHours(weekday)
	return hours != nil && len(hours) == 1 && hours[0] == HoursClosed
}

func (h *HoursHelper) HoursAreOpen24Hours(weekday Weekday) bool {
	var hours = h.GetHours(weekday)
	return hours != nil && len(hours) == 1 && hours[0] == HoursOpen24Hours
}

func ParseAndFormatHours(tFormat string, openHours string, closeHours string) (string, error) {
	openTime, err := time.Parse(tFormat, openHours)
	if err != nil {
		return "", fmt.Errorf("Error parsing hours %s with format %s: %s", openHours, tFormat, err)
	}
	closeTime, err := time.Parse(tFormat, closeHours)
	if err != nil {
		return "", fmt.Errorf("Error parsing hours %s with format %s: %s", closeHours, tFormat, err)
	}
	return fmt.Sprintf("%s:%s", openTime.Format("15:04"), closeTime.Format("15:04")), nil
}

func ConvertBetweenFormats(hours string, convertFromFormat string, convertToFormat string) (string, error) {
	t, err := time.Parse(convertFromFormat, hours)
	if err != nil {
		return "", fmt.Errorf("Hours %s was not in expected format %s: %s", hours, convertFromFormat, err)
	}
	return t.Format(convertToFormat), nil
}
<|endoftext|>


<commit_before>// Photobomb conducts workflow tests triggered by requests to its web server.
package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"

	log "github.com/Sirupsen/logrus"
	sdk "github.com/dysolution/espsdk"
)

const NAME = "photobomb"
const VERSION = "0.0.1"

var appID = fmt.Sprintf("%s %s", NAME, VERSION)

var client sdk.Client
var token sdk.Token
var config Raid

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func runServer() {
	http.HandleFunc("/", usage)
	http.HandleFunc("/example", showExampleConfig)
	http.HandleFunc("/config", showConfig)
	http.HandleFunc("/execute", execute)

	tcpSocket := ":8080"
	log.Infof("listening on %s", tcpSocket)
	http.ListenAndServe(tcpSocket, nil)
}

func loadConfig(path string) Raid {
	fi, err := os.Stat(path)
	check(err)
	if fi.Size() == 0 {
		// The user has probably tried to redirect "example" output, e.g.,
		// photobomb example > config.json, which zeroes out config.json, so
		// we shouldn't bother trying to read it.
		return ExampleConfig()
	}

	log.Debugf("reading configuration from: %s", path)
	file, err := ioutil.ReadFile(path)
	if err != nil {
		log.Debugf("could not read config: %s", path)
		return ExampleConfig()
	}

	var data Raid
	if err := json.Unmarshal(file, &data); err != nil {
		log.Fatal(err)
	}
	check(err)
	return data
}

func showConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(config, "", "    ")
	check(err)
	w.Write(output)
}

func showExampleConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(ExampleConfig(), "", "    ")
	check(err)
	w.Write(output)
}

func execute(w http.ResponseWriter, r *http.Request) {
	raid := config
	summary := raid.Conduct()
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(summary)
}

func usage(w http.ResponseWriter, r *http.Request) {
	usage := []byte(`
Paths:

/          display this message
/example   display an example config
/config    display the current config
/execute   execute the current config

Configuration:

`)
	output, err := json.MarshalIndent(config, "", "    ")
	check(err)
	usage = append(usage, output...)
	w.Write(usage)
}
<commit_msg>output HTML on root route<commit_after>// Photobomb conducts workflow tests triggered by requests to its web server.
package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"

	log "github.com/Sirupsen/logrus"
	sdk "github.com/dysolution/espsdk"
)

const NAME = "photobomb"
const VERSION = "0.0.1"

var appID = fmt.Sprintf("%s %s", NAME, VERSION)

var client sdk.Client
var token sdk.Token
var config Raid

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func runServer() {
	http.HandleFunc("/", usage)
	http.HandleFunc("/example", showExampleConfig)
	http.HandleFunc("/config", showConfig)
	http.HandleFunc("/execute", execute)

	tcpSocket := ":8080"
	log.Infof("listening on %s", tcpSocket)
	http.ListenAndServe(tcpSocket, nil)
}

func loadConfig(path string) Raid {
	fi, err := os.Stat(path)
	check(err)
	if fi.Size() == 0 {
		// The user has probably tried to redirect "example" output, e.g.,
		// photobomb example > config.json, which zeroes out config.json, so
		// we shouldn't bother trying to read it.
		return ExampleConfig()
	}

	log.Debugf("reading configuration from: %s", path)
	file, err := ioutil.ReadFile(path)
	if err != nil {
		log.Debugf("could not read config: %s", path)
		return ExampleConfig()
	}

	var data Raid
	if err := json.Unmarshal(file, &data); err != nil {
		log.Fatal(err)
	}
	check(err)
	return data
}

func showConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(config, "", "    ")
	check(err)
	w.Write(output)
}

func showExampleConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(ExampleConfig(), "", "    ")
	check(err)
	w.Write(output)
}

func execute(w http.ResponseWriter, r *http.Request) {
	raid := config
	summary := raid.Conduct()
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(summary)
}

func usage(w http.ResponseWriter, r *http.Request) {
	const tpl = `
<!DOCTYPE html>

		<meta charset="UTF-8">
		<title>{{.AppName}}</title>

Routes

Path	Action

		{{range $path, $helpText := .Routes}}
{{ $path }}	{{ $helpText }}
{{end}}

Configuration

{{ .Config }}

`

	t, err := template.New("webpage").Parse(tpl)
	routes := make(map[string]string)
	routes["/"] = "display this message"
	routes["/example"] = "display an example config"
	routes["/config"] = "display the current config"
	routes["/execute"] = "execute the current config"

	output, err := json.MarshalIndent(config, "", "    ")
	check(err)

	data := struct {
		AppName string
		Routes  map[string]string
		Config  string
	}{
		AppName: appID,
		Routes:  routes,
		Config:  string(output),
	}

	err = t.Execute(w, data)
	check(err)
}
<|endoftext|>


<commit_before>package photosync

import (
	"fmt"
	"log"
	"time"
	"os"
	"os/exec"
	"github.com/garyburd/go-oauth/oauth"
	"io/ioutil"
	"encoding/json"
	"path/filepath"
	"strings"
	"github.com/go-fsnotify/fsnotify"
)

// time formats
const FilenameTimeLayout = "20060102_150405"
const ExifTimeLayout = "2006:01:02 15:04:05"
const FlickrTimeLayout = "2006-01-02 15:04:05"

type Options struct {
	ConfigPath string
	Dryrun bool
	NoUpload bool
	Daemon bool
	RetroTags bool
}

type PhotosMap map[string]Photo

type OauthConfig struct {
	Consumer oauth.Credentials
	Access oauth.Credentials
}

type PhotosyncConfig struct {
	OauthConfig
	Filenames []FilenameConfig `json:"filenames"`
	WatchDir []WatchDirConfig `json:"directories"`
	FilenameTimeFormats []FilenameTimeFormat `json:"filename_time_formats"`
}

type FilenameTimeFormat struct {
	Format string
	Prefix []string
	Postfix []string
}

type ExifToolOutput struct {
	SourceFile string
	ExifTool struct {
		Warning string
	}
	Ifd struct {
		Orientation string
		Make string
		Model string
		ModifyDate string
	} `json:"IFD0"`
}

// Load the consumer key and secret in from the config file
func LoadConfig(configPath *string,config *PhotosyncConfig) error {
	b, err := ioutil.ReadFile(*configPath)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, config); err != nil {
		return err
	}

	// precompile the filename regexps
	for i := 0; i < len(config.Filenames); i++ {
		config.Filenames[i].Load()
	}

	// create the templates
	for i := 0; i < len(config.WatchDir); i++ {
		config.WatchDir[i].CreateTemplates()
	}

	return nil
}

func Sync(api *FlickrAPI, photos *PhotosMap, videos *PhotosMap, opt *Options) (int, int, int, int, error) {
	renCnt := 0
	exCnt := 0
	upCnt := 0
	errCnt := 0

	for _, dir := range api.config.WatchDir {
		// ensure the path exists
		if _, err := os.Stat(dir.Dir); os.IsNotExist(err) {
				fmt.Printf("no such file or directory: %s", dir.Dir)
				continue
		}

		exifAry, er := GetAllExifData(dir.Dir)
		if er != nil {
			return renCnt, exCnt, upCnt, errCnt, er
		}

		exifs := make(map[string]ExifToolOutput)
		for _, ex := range *exifAry {
			exifs[ex.SourceFile] = ex
		}

		err := filepath.Walk(dir.Dir, func (path string, f os.FileInfo, err error) error {
			return processFile(api, &dir, path, f, &exifs, photos, videos, &renCnt, &exCnt, &upCnt, &errCnt, opt)
		})

		if err != nil {
			return renCnt, exCnt, upCnt, errCnt, err
		}
	}

	// start the daemon
	if opt.Daemon {
		log.Println("starting...")

		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			log.Fatal(err)
		}

		done := make(chan bool)
		dirCfgs := make(map[string]WatchDirConfig)

		go func() {
			for {
				select {
				case event := <-watcher.Events:
					//log.Println("event:", event)
					if event.Op & fsnotify.Create == fsnotify.Create {
						log.Println("created file:", event.Name)
						f, err := os.Stat(event.Name)
						if err != nil {
							log.Println("error getting file info for ", event.Name)
							continue
						}

						// get the dir config
						var cfg *WatchDirConfig
						for dir, dirCfg := range dirCfgs {
							if event.Name[:len(dir)] == dir {
								cfg = &dirCfg
							}
						}

						processFile(api, cfg, event.Name, f, nil, photos, videos, &renCnt, &exCnt, &upCnt, &errCnt, opt)
					}
				case err := <-watcher.Errors:
					log.Println("error:", err)
				}
			}
		}()

		for _, dir := range api.config.WatchDir {
			// ensure the path exists
			if _, err := os.Stat(dir.Dir); os.IsNotExist(err) {
					fmt.Printf("no such file or directory: %s", dir.Dir)
					continue
			}

			dirCfgs[dir.Dir] = dir // add to map of configs for the event handler to use

			err = watcher.Add(dir.Dir)
			if err != nil {
				log.Fatal(err)
			}

		}

		<-done

	}

	return renCnt, exCnt, upCnt, errCnt, nil
}

func processFile(api *FlickrAPI, dir *WatchDirConfig, path string, f os.FileInfo, exifs *map[string]ExifToolOutput, photos, videos *PhotosMap, renCnt, exCnt, upCnt, errCnt *int, opt *Options) error {
	if !f.IsDir() { // make sure we aren't operating on a directory

		var newPath, newKey string
		var changed bool
		_, fname := filepath.Split(path)
		ext := filepath.Ext(fname)
		extUpper := strings.ToUpper(ext)
		key := fname[:len(fname)-len(ext)]

		// rename file if needed
		// check again all filename configs
		for _, fncfg := range api.GetFilenamesConfig() {
			var exif ExifToolOutput
			if exifs != nil {
				exif = (*exifs)[path]
			}
			newPath, newKey, changed = fncfg.GetNewPath(path, &exif)
			if changed {
				fmt.Println("rename to:", newPath)

				if !opt.Dryrun {
					os.Rename(path,newPath)
					path = newPath // swith to the new file
					key = newKey
					*renCnt++
				}

				break // found our match to bail
			}
		}

		if extUpper == ".JPG" || extUpper == ".MOV" || extUpper == ".MP4" {
			fmt.Println(path)

			var exists bool
			var exPhoto Photo

			if extUpper == ".JPG" {
				exPhoto, exists = (*photos)[key]
			} else if extUpper == ".MOV" || extUpper == ".MP4" {
				exPhoto, exists = (*videos)[key]
			}

			if !exists {
				fmt.Print("|=====")

				if !opt.Dryrun && !opt.NoUpload {
					tmppath, done, er := FixExif(key, path, f)

					path = tmppath // update the path to the potentially new path
					if er != nil { *errCnt++; return nil }
					res, err := api.Upload(path, f)
					if err != nil { *errCnt++; return nil }

					defer done(api, res.PhotoId)

					// set the tags in config
					if len(dir.Tags) > 0 {
						api.AddTags(res.PhotoId, dir.Tags)
					}

					// add back in to photos and videos
					newPhoto := Photo{
						Id: res.PhotoId,
						Owner: "",
						Secret: "",
						Title: key,
					}

					switch extUpper {
					case ".JPG":
						(*photos)[key] = newPhoto
					case ".MOV":
						fallthrough
					case ".MP4":
						(*videos)[key] = newPhoto
					}

					fmt.Println("=====| 100%")
				} else {
					fmt.Println("=====| 100% --+ dry run +--")
				}

				*upCnt++
			} else {
				// still apply retroactive tags
				if opt.RetroTags && len(dir.Tags) > 0 {
					fmt.Print("assign tags: ",dir.Tags)
					if !opt.Dryrun && !opt.NoUpload {
						api.AddTags(exPhoto.Id, dir.Tags)
					} else {
						fmt.Print(" --+ dry run +--")
					}
					fmt.Println()
				}

				*exCnt++
			}
		}
	}

	return nil
}

func getTimeFromTitle(api *FlickrAPI, title string) (*time.Time, error) {
	for _, tf := range api.config.FilenameTimeFormats {
		var tmp = title

		// check prefix
		for _, p := range tf.Prefix {
			if tmp[:len(p)] == p {
				tmp = tmp[len(p):]
				break // we found our prefix
			}
		}

		// check postfix
		for _, p := range tf.Postfix {
			if tmp[len(tmp)-len(p):] == p {
				tmp = tmp[:len(tmp)-len(p)]
				break // we found our postfix
			}
		}
		//fmt.Println("using title",tmp)

		// parse what's left
		t, err := time.Parse(tf.Format, tmp)
		if err == nil { return &t, nil }
	}
	return nil, Error{"no timestamp in title"}
}

func GetExifData(path string) (*ExifToolOutput, error) {
	exifs, err := GetAllExifData(path)
	if err != nil {
		return nil, err
	}
	if len(*exifs) == 0 {
		return nil, Error{ "no exif data found" }
	}
	return &(*exifs)[0], nil
}

func GetAllExifData(path string) (*[]ExifToolOutput, error) {
	out, err := exec.Command("exiftool","-a","-u","-g1","-json","-r",path).Output()
	foo := string(out)
	final := ""
	// kill new lines
	final = strings.Replace(foo, "\n", "", -1)
	out = []byte(final)
	if err != nil { return nil, err }

	var exif []ExifToolOutput
	if err := json.Unmarshal(out, &exif); err != nil {
		log.Fatal(final)
		log.Fatal("unmarshal error: ",err)
		return nil, err
	}

	return &exif, nil
}

//
// Checks the EXIF data for JPGs and returns the path to either the original or the fixed JPG file.
// The 2nd return value should be called when use of the JPG is complete.
// workingFile, done, err := FixExif(...)
// defer done()
//
func FixExif(title string, path string, f os.FileInfo) (string, func(api *FlickrAPI, photoId string), error) {
	ext := filepath.Ext(f.Name())
	extUpper := strings.ToUpper(ext)
	var timeFromFilename *time.Time

	_setDateTaken := func(api *FlickrAPI, photoId string) {
		var err error
		timeFromFilename, err = getTimeFromTitle(api, title)
		if err != nil { timeFromFilename = nil }

		// check the file name
		if timeFromFilename != nil {
			fmt.Printf("set time from file name: %s\n",timeFromFilename.Format(FlickrTimeLayout))
			api.SetDate(photoId, timeFromFilename.Format(FlickrTimeLayout)) // eat the error as this is optional
		}
	}

	_setDateTakenMov := func(api *FlickrAPI, photoId string) {
		// they are done uploading the file so let's set it's date
		_setDateTaken(api, photoId)

		if timeFromFilename == nil {
			// fall back to the mod time
			// we do this for MOV's because there isn't exif data to use
			fmt.Printf("set time to: %s\n",f.ModTime().Format(FlickrTimeLayout))
			api.SetDate(photoId, f.ModTime().Format(FlickrTimeLayout)) // eat the error as this is optional
		}
	}

	if extUpper == ".JPG" {
		// check for valid exif data
		exif, err := GetExifData(path)
		if err != nil { return "", _setDateTaken, err }

		if len(exif.ExifTool.Warning) > 0 {
			// we have an exif error
			if len(exif.Ifd.ModifyDate) > 0 {
				// we have a valid date already so just fix exif

				// create tmp file and copy
				tmpfile, err := ioutil.TempFile("",f.Name()+".")
				if err != nil { return "", _setDateTaken, err }

				tmpfilePath := tmpfile.Name() // ensure it's a new file for the sake of
				os.Remove(tmpfile.Name())

				_, errr := exec.Command("exiftool","-exif:all=","-tagsfromfile","@","-all:all","-unsafe","-o",tmpfilePath,path).CombinedOutput()
				if errr != nil { return "", _setDateTaken, errr }

				// return the callback function that should get called when use of this image is complete
				return tmpfilePath, func(api *FlickrAPI, photoId string) {os.Remove(tmpfilePath) }, errr
			}
		}

	} else if extUpper == ".MOV" || extUpper == ".MP4" {
		// always set to the file's modified date
		return path, _setDateTakenMov, nil
	}

	return path, _setDateTaken, nil
}
<commit_msg>update the os.FileInfo when renaming<commit_after>package photosync

import (
	"fmt"
	"log"
	"time"
	"os"
	"os/exec"
	"github.com/garyburd/go-oauth/oauth"
	"io/ioutil"
	"encoding/json"
	"path/filepath"
	"strings"
	"github.com/go-fsnotify/fsnotify"
)

// time formats
const FilenameTimeLayout = "20060102_150405"
const ExifTimeLayout = "2006:01:02 15:04:05"
const FlickrTimeLayout = "2006-01-02 15:04:05"

type Options struct {
	ConfigPath string
	Dryrun bool
	NoUpload bool
	Daemon bool
	RetroTags bool
}

type PhotosMap map[string]Photo

type OauthConfig struct {
	Consumer oauth.Credentials
	Access oauth.Credentials
}

type PhotosyncConfig struct {
	OauthConfig
	Filenames []FilenameConfig `json:"filenames"`
	WatchDir []WatchDirConfig `json:"directories"`
	FilenameTimeFormats []FilenameTimeFormat `json:"filename_time_formats"`
}

type FilenameTimeFormat struct {
	Format string
	Prefix []string
	Postfix []string
}

type ExifToolOutput struct {
	SourceFile string
	ExifTool struct {
		Warning string
	}
	Ifd struct {
		Orientation string
		Make string
		Model string
		ModifyDate string
	} `json:"IFD0"`
}

// Load the consumer key and secret in from the config file
func LoadConfig(configPath *string,config *PhotosyncConfig) error {
	b, err := ioutil.ReadFile(*configPath)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, config); err != nil {
		return err
	}

	// precompile the filename regexps
	for i := 0; i < len(config.Filenames); i++ {
		config.Filenames[i].Load()
	}

	// create the templates
	for i := 0; i < len(config.WatchDir); i++ {
		config.WatchDir[i].CreateTemplates()
	}

	return nil
}

func Sync(api *FlickrAPI, photos *PhotosMap, videos *PhotosMap, opt *Options) (int, int, int, int, error) {
	renCnt := 0
	exCnt := 0
	upCnt := 0
	errCnt := 0

	for _, dir := range api.config.WatchDir {
		// ensure the path exists
		if _, err := os.Stat(dir.Dir); os.IsNotExist(err) {
				fmt.Printf("no such file or directory: %s", dir.Dir)
				continue
		}

		exifAry, er := GetAllExifData(dir.Dir)
		if er != nil {
			return renCnt, exCnt, upCnt, errCnt, er
		}

		exifs := make(map[string]ExifToolOutput)
		for _, ex := range *exifAry {
			exifs[ex.SourceFile] = ex
		}

		err := filepath.Walk(dir.Dir, func (path string, f os.FileInfo, err error) error {
			return processFile(api, &dir, path, f, &exifs, photos, videos, &renCnt, &exCnt, &upCnt, &errCnt, opt)
		})

		if err != nil {
			return renCnt, exCnt, upCnt, errCnt, err
		}
	}

	// start the daemon
	if opt.Daemon {
		log.Println("starting...")

		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			log.Fatal(err)
		}

		done := make(chan bool)
		dirCfgs := make(map[string]WatchDirConfig)

		go func() {
			for {
				select {
				case event := <-watcher.Events:
					//log.Println("event:", event)
					if event.Op & fsnotify.Create == fsnotify.Create {
						log.Println("created file:", event.Name)
						f, err := os.Stat(event.Name)
						if err != nil {
							log.Println("error getting file info for ", event.Name)
							continue
						}

						// get the dir config
						var cfg *WatchDirConfig
						for dir, dirCfg := range dirCfgs {
							if event.Name[:len(dir)] == dir {
								cfg = &dirCfg
							}
						}

						processFile(api, cfg, event.Name, f, nil, photos, videos, &renCnt, &exCnt, &upCnt, &errCnt, opt)
					}
				case err := <-watcher.Errors:
					log.Println("error:", err)
				}
			}
		}()

		for _, dir := range api.config.WatchDir {
			// ensure the path exists
			if _, err := os.Stat(dir.Dir); os.IsNotExist(err) {
					fmt.Printf("no such file or directory: %s", dir.Dir)
					continue
			}

			dirCfgs[dir.Dir] = dir // add to map of configs for the event handler to use

			err = watcher.Add(dir.Dir)
			if err != nil {
				log.Fatal(err)
			}

		}

		<-done

	}

	return renCnt, exCnt, upCnt, errCnt, nil
}

func processFile(api *FlickrAPI, dir *WatchDirConfig, path string, f os.FileInfo, exifs *map[string]ExifToolOutput, photos, videos *PhotosMap, renCnt, exCnt, upCnt, errCnt *int, opt *Options) error {
	if !f.IsDir() { // make sure we aren't operating on a directory

		var newPath, newKey string
		var changed bool
		_, fname := filepath.Split(path)
		ext := filepath.Ext(fname)
		extUpper := strings.ToUpper(ext)
		key := fname[:len(fname)-len(ext)]

		// rename file if needed
		// check again all filename configs
		for _, fncfg := range api.GetFilenamesConfig() {
			var exif ExifToolOutput
			if exifs != nil {
				exif = (*exifs)[path]
			}
			newPath, newKey, changed = fncfg.GetNewPath(path, &exif)
			if changed {
				fmt.Println("rename to:", newPath)

				if !opt.Dryrun {
					os.Rename(path,newPath)
					path = newPath // swith to the new file
					key = newKey
					var err error
					f, err = os.Stat(path)
					if err != nil { return err }
					*renCnt++
				}

				break // found our match to bail
			}
		}

		if extUpper == ".JPG" || extUpper == ".MOV" || extUpper == ".MP4" {
			fmt.Println(path)

			var exists bool
			var exPhoto Photo

			if extUpper == ".JPG" {
				exPhoto, exists = (*photos)[key]
			} else if extUpper == ".MOV" || extUpper == ".MP4" {
				exPhoto, exists = (*videos)[key]
			}

			if !exists {
				fmt.Print("|=====")

				if !opt.Dryrun && !opt.NoUpload {
					tmppath, done, er := FixExif(key, path, f)

					path = tmppath // update the path to the potentially new path
					if er != nil { *errCnt++; return nil }
					res, err := api.Upload(path, f)
					if err != nil { *errCnt++; return nil }

					defer done(api, res.PhotoId)

					// set the tags in config
					if len(dir.Tags) > 0 {
						api.AddTags(res.PhotoId, dir.Tags)
					}

					// add back in to photos and videos
					newPhoto := Photo{
						Id: res.PhotoId,
						Owner: "",
						Secret: "",
						Title: key,
					}

					switch extUpper {
					case ".JPG":
						(*photos)[key] = newPhoto
					case ".MOV":
						fallthrough
					case ".MP4":
						(*videos)[key] = newPhoto
					}

					fmt.Println("=====| 100%")
				} else {
					fmt.Println("=====| 100% --+ dry run +--")
				}

				*upCnt++
			} else {
				// still apply retroactive tags
				if opt.RetroTags && len(dir.Tags) > 0 {
					fmt.Print("assign tags: ",dir.Tags)
					if !opt.Dryrun && !opt.NoUpload {
						api.AddTags(exPhoto.Id, dir.Tags)
					} else {
						fmt.Print(" --+ dry run +--")
					}
					fmt.Println()
				}

				*exCnt++
			}
		}
	}

	return nil
}

func getTimeFromTitle(api *FlickrAPI, title string) (*time.Time, error) {
	for _, tf := range api.config.FilenameTimeFormats {
		var tmp = title

		// check prefix
		for _, p := range tf.Prefix {
			if tmp[:len(p)] == p {
				tmp = tmp[len(p):]
				break // we found our prefix
			}
		}

		// check postfix
		for _, p := range tf.Postfix {
			if tmp[len(tmp)-len(p):] == p {
				tmp = tmp[:len(tmp)-len(p)]
				break // we found our postfix
			}
		}
		//fmt.Println("using title",tmp)

		// parse what's left
		t, err := time.Parse(tf.Format, tmp)
		if err == nil { return &t, nil }
	}
	return nil, Error{"no timestamp in title"}
}

func GetExifData(path string) (*ExifToolOutput, error) {
	exifs, err := GetAllExifData(path)
	if err != nil {
		return nil, err
	}
	if len(*exifs) == 0 {
		return nil, Error{ "no exif data found" }
	}
	return &(*exifs)[0], nil
}

func GetAllExifData(path string) (*[]ExifToolOutput, error) {
	out, err := exec.Command("exiftool","-a","-u","-g1","-json","-r",path).Output()
	foo := string(out)
	final := ""
	// kill new lines
	final = strings.Replace(foo, "\n", "", -1)
	out = []byte(final)
	if err != nil { return nil, err }

	var exif []ExifToolOutput
	if err := json.Unmarshal(out, &exif); err != nil {
		log.Fatal(final)
		log.Fatal("unmarshal error: ",err)
		return nil, err
	}

	return &exif, nil
}

//
// Checks the EXIF data for JPGs and returns the path to either the original or the fixed JPG file.
// The 2nd return value should be called when use of the JPG is complete.
// workingFile, done, err := FixExif(...)
// defer done()
//
func FixExif(title string, path string, f os.FileInfo) (string, func(api *FlickrAPI, photoId string), error) {
	ext := filepath.Ext(f.Name())
	extUpper := strings.ToUpper(ext)
	var timeFromFilename *time.Time

	_setDateTaken := func(api *FlickrAPI, photoId string) {
		var err error
		timeFromFilename, err = getTimeFromTitle(api, title)
		if err != nil { timeFromFilename = nil }

		// check the file name
		if timeFromFilename != nil {
			fmt.Printf("set time from file name: %s\n",timeFromFilename.Format(FlickrTimeLayout))
			api.SetDate(photoId, timeFromFilename.Format(FlickrTimeLayout)) // eat the error as this is optional
		}
	}

	_setDateTakenMov := func(api *FlickrAPI, photoId string) {
		// they are done uploading the file so let's set it's date
		_setDateTaken(api, photoId)

		if timeFromFilename == nil {
			// fall back to the mod time
			// we do this for MOV's because there isn't exif data to use
			fmt.Printf("set time to: %s\n",f.ModTime().Format(FlickrTimeLayout))
			api.SetDate(photoId, f.ModTime().Format(FlickrTimeLayout)) // eat the error as this is optional
		}
	}

	if extUpper == ".JPG" {
		// check for valid exif data
		exif, err := GetExifData(path)
		if err != nil { return "", _setDateTaken, err }

		if len(exif.ExifTool.Warning) > 0 {
			// we have an exif error
			if len(exif.Ifd.ModifyDate) > 0 {
				// we have a valid date already so just fix exif

				// create tmp file and copy
				tmpfile, err := ioutil.TempFile("",f.Name()+".")
				if err != nil { return "", _setDateTaken, err }

				tmpfilePath := tmpfile.Name() // ensure it's a new file for the sake of
				os.Remove(tmpfile.Name())

				_, errr := exec.Command("exiftool","-exif:all=","-tagsfromfile","@","-all:all","-unsafe","-o",tmpfilePath,path).CombinedOutput()
				if errr != nil { return "", _setDateTaken, errr }

				// return the callback function that should get called when use of this image is complete
				return tmpfilePath, func(api *FlickrAPI, photoId string) {os.Remove(tmpfilePath) }, errr
			}
		}

	} else if extUpper == ".MOV" || extUpper == ".MP4" {
		// always set to the file's modified date
		return path, _setDateTakenMov, nil
	}

	return path, _setDateTaken, nil
}
<|endoftext|>


<commit_before>package block

import (
	"encoding/binary"
	"math"
	"os"
	"testing"
	"time"

	"github.com/kadirahq/go-tools/segmmap"
)

var (
	tmpdir  = "/tmp/test-segmmap/"
	tmpfile = tmpdir + "file_"
)

func setup(t *testing.T) {
	if err := os.RemoveAll(tmpdir); err != nil {
		t.Fatal(err)
	}

	if err := os.MkdirAll(tmpdir, 0777); err != nil {
		t.Fatal(err)
	}
}

func clear(t *testing.T) {
	time.Sleep(time.Second) // segmmap may add one more segfile before removing
	// directory, which gives "directory not empty". So wait a second for it.

	if err := os.RemoveAll(tmpdir); err != nil {
		t.Fatal(err)
	}
}

func TestFromByteArr(t *testing.T) {
	dummyLen, dummyCap := 120, 1200

	dummySlice := make([]byte, dummyLen, dummyCap)

	pSlice := fromByteSlice(dummySlice)

	if len(pSlice) != dummyLen/pointsz || cap(pSlice) != dummyCap/pointsz {
		t.Fatal("Pointer slice have a wrong lenth or capacity.")
	}

	dummyPoint := Point{
		Total: 3.141592,
		Count: 10,
	}

	(pSlice)[1] = dummyPoint

	bits := binary.LittleEndian.Uint64(dummySlice[16:24])
	total := math.Float64frombits(bits)

	if total != dummyPoint.Total {
		t.Fatal("Total is not set properly.")
	}
}

func TestReadRecords(t *testing.T) {
	setup(t)
	defer clear(t)

	dummySegmapSize := int64(96 * 5)

	m, err := segmmap.NewMap(tmpfile, dummySegmapSize)

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

	testBlock := Block{
		recs: [][]Point{},
		mmap: m,
		rbs:  96,
	}

	m.Load(int64(0))

	bits := math.Float64bits(3.14)
	bytes := make([]byte, 8)
	binary.LittleEndian.PutUint64(bytes, bits)

	// Write at 2nd point of first record
	if n, err := m.WriteAt(bytes, 16); err != nil {
		t.Fatal(err)
	} else if n != 8 {
		t.Fatal("Wrong size")
	}

	testBlock.readRecords()

	if int64(len(testBlock.recs)) != dummySegmapSize/testBlock.rbs {
		t.Fatal("Wrong length in Block Records")
	}

	record := testBlock.recs[0] // first record
	point := record[1]          // second point

	if point.Total != 3.14 {
		t.Fatal("Wrong data in Block Record")
	}
}

func TestNewBlock(t *testing.T) {
	setup(t)
	defer clear(t)

	block, err := New(tmpdir, 2)

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

	if len(block.recs) != 0 {
		t.Fatal("Wrong length")
	}
}

func TestAdd(t *testing.T) {
	setup(t)
	defer clear(t)

	testRecordSize := int64(100)

	block, err := New(tmpdir, testRecordSize)
	if err != nil {
		t.Fatal(err)
	}

	if len(block.recs) != 0 {
		t.Fatal("Wrong length")
	}

	err = block.Track(0, 0, 123.456, 5)
	if err != nil {
		t.Fatal(err)
	}

	err = block.Track(7, 3, 123.456, 5)
	if err != nil {
		t.Fatal(err)
	}

	// Find expected value for Record length
	testRecordByteSize := (testRecordSize * pointsz)
	expectedRLen := segsz / testRecordByteSize

	if int64(len(block.recs)) != expectedRLen {
		t.Fatal("Wrong length. Expected:", expectedRLen,
			"Got:", len(block.recs))
	}

	// It should be able to write to an index larger than seg size.
	err = block.Track(expectedRLen, 0, 123.456, 5)

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

	if int64(len(block.recs)) != 2*expectedRLen {
		t.Fatal("Wrong length")
	}

	if block.recs[0][0].Total != 123.456 ||
		block.recs[7][3].Total != 123.456 ||
		block.recs[expectedRLen][0].Total != 123.456 {
		t.Fatal("Total not set correctly")
	}

	if block.recs[0][0].Count != 5 ||
		block.recs[7][3].Count != 5 ||
		block.recs[expectedRLen][0].Count != 5 {
		t.Fatal("Count not set correctly")
	}
}
<commit_msg>Benchmark test for block<commit_after>package block

import (
	"encoding/binary"
	"math"
	"os"
	"sync/atomic"
	"testing"
	"time"

	"github.com/kadirahq/go-tools/segmmap"
)

var (
	tmpdir  = "/tmp/test-segmmap/"
	tmpfile = tmpdir + "file_"
)

func setup(t *testing.T) {
	if err := os.RemoveAll(tmpdir); err != nil {
		t.Fatal(err)
	}

	if err := os.MkdirAll(tmpdir, 0777); err != nil {
		t.Fatal(err)
	}
}

func clear(t *testing.T) {
	time.Sleep(time.Second) // segmmap may add one more segfile before removing
	// directory, which gives "directory not empty". So wait a second for it.

	if err := os.RemoveAll(tmpdir); err != nil {
		t.Fatal(err)
	}
}

func TestFromByteArr(t *testing.T) {
	dummyLen, dummyCap := 120, 1200

	dummySlice := make([]byte, dummyLen, dummyCap)

	pSlice := fromByteSlice(dummySlice)

	if len(pSlice) != dummyLen/pointsz || cap(pSlice) != dummyCap/pointsz {
		t.Fatal("Pointer slice have a wrong lenth or capacity.")
	}

	dummyPoint := Point{
		Total: 3.141592,
		Count: 10,
	}

	(pSlice)[1] = dummyPoint

	bits := binary.LittleEndian.Uint64(dummySlice[16:24])
	total := math.Float64frombits(bits)

	if total != dummyPoint.Total {
		t.Fatal("Total is not set properly.")
	}
}

func TestReadRecords(t *testing.T) {
	setup(t)
	defer clear(t)

	dummySegmapSize := int64(96 * 5)

	m, err := segmmap.NewMap(tmpfile, dummySegmapSize)

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

	testBlock := Block{
		recs: [][]Point{},
		mmap: m,
		rbs:  96,
	}

	m.Load(int64(0))

	bits := math.Float64bits(3.14)
	bytes := make([]byte, 8)
	binary.LittleEndian.PutUint64(bytes, bits)

	// Write at 2nd point of first record
	if n, err := m.WriteAt(bytes, 16); err != nil {
		t.Fatal(err)
	} else if n != 8 {
		t.Fatal("Wrong size")
	}

	testBlock.readRecords()

	if int64(len(testBlock.recs)) != dummySegmapSize/testBlock.rbs {
		t.Fatal("Wrong length in Block Records")
	}

	record := testBlock.recs[0] // first record
	point := record[1]          // second point

	if point.Total != 3.14 {
		t.Fatal("Wrong data in Block Record")
	}
}

func TestNewBlock(t *testing.T) {
	setup(t)
	defer clear(t)

	block, err := New(tmpdir, 2)

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

	if len(block.recs) != 0 {
		t.Fatal("Wrong length")
	}
}

func TestTrack(t *testing.T) {
	setup(t)
	defer clear(t)

	testRecordSize := int64(100)

	block, err := New(tmpdir, testRecordSize)
	if err != nil {
		t.Fatal(err)
	}

	if len(block.recs) != 0 {
		t.Fatal("Wrong length")
	}

	err = block.Track(0, 0, 123.456, 5)
	if err != nil {
		t.Fatal(err)
	}

	err = block.Track(7, 3, 123.456, 5)
	if err != nil {
		t.Fatal(err)
	}

	// Find expected value for Record length
	testRecordByteSize := (testRecordSize * pointsz)
	expectedRLen := segsz / testRecordByteSize

	if int64(len(block.recs)) != expectedRLen {
		t.Fatal("Wrong length. Expected:", expectedRLen,
			"Got:", len(block.recs))
	}

	// It should be able to write to an index larger than seg size.
	err = block.Track(expectedRLen, 0, 123.456, 5)

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

	if int64(len(block.recs)) != 2*expectedRLen {
		t.Fatal("Wrong length")
	}

	if block.recs[0][0].Total != 123.456 ||
		block.recs[7][3].Total != 123.456 ||
		block.recs[expectedRLen][0].Total != 123.456 {
		t.Fatal("Total not set correctly")
	}

	if block.recs[0][0].Count != 5 ||
		block.recs[7][3].Count != 5 ||
		block.recs[expectedRLen][0].Count != 5 {
		t.Fatal("Count not set correctly")
	}
}

func BenchmarkTrack(b *testing.B) {
	dir := "/tmp/test_block/"

	if err := os.RemoveAll(dir); err != nil {
		b.Fatal(err)
	}
	if err := os.MkdirAll(dir, 0777); err != nil {
		b.Fatal(err)
	}

	bl, err := New(dir, 100)

	if err != nil {
		b.Fatal("Error when creating block", err)
	}

	pid := int64(-1)

	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			lpid := atomic.AddInt64(&pid, 1)
			bl.Track(lpid/100, lpid%100, 4.5, 1)
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net"
	"strings"

	"net/url"
)

var (
	host, user, password, endpoint string
	port                           int
	debug                          bool = false
)

func executeCommand(nivis_ami_uri, command string) ([]string, error) {
	result := []string{}

	fmt.Println("C1")

	u, err := url.Parse(nivis_ami_uri)
	if err != nil {
		return result, err
	}
	fmt.Println("C2")
	password, _ := u.User.Password()
	user := u.User.Username()
	hostPort := fmt.Sprintf("%s:%d", u.Host, 5038)

	c, err := net.Dial("tcp", hostPort)
	if err != nil {
		return result, err
	}
	defer c.Close()
	fmt.Println("C3")

	fmt.Fprintf(c, "Action: login\r\n")
	fmt.Fprintf(c, "Username: %s\r\n", user)
	fmt.Fprintf(c, "Secret: %s\r\n", password)
	fmt.Fprintf(c, "\r\n")
	fmt.Println("C4")

	fmt.Fprintf(c, "Action: command\r\n")
	fmt.Fprintf(c, "Command: %s\r\n", command)
	fmt.Fprintf(c, "\r\n")
	fmt.Println("C5")

	connbuf := bufio.NewReader(c)
	for {
		str, err := connbuf.ReadString('\n')
		fmt.Println("C6", str)
		if len(str) > 0 {
			if str == "--END COMMAND--\r\n" {
				break
			}
			result = append(result, str)
		}
		if err != nil {
			return []string{}, err
		}
	}
	return result, nil
}

func receiveEvents(nivis_ami_uri string) error {
	result := []string{}

	u, err := url.Parse(nivis_ami_uri)
	if err != nil {
		return err
	}
	password, _ := u.User.Password()
	user := u.User.Username()
	hostPort := fmt.Sprintf("%s:%d", u.Host, 5038)

	c, err := net.Dial("tcp", hostPort)
	if err != nil {
		return err
	}
	defer c.Close()

	fmt.Fprintf(c, "Action: login\r\n")
	fmt.Fprintf(c, "Username: %s\r\n", user)
	fmt.Fprintf(c, "Secret: %s\r\n", password)
	fmt.Fprintf(c, "\r\n")

	connbuf := bufio.NewReader(c)
	for {
		str, err := connbuf.ReadString('\n')
		if len(str) > 0 {
			if str == "--END COMMAND--\r\n" {
				break
			}
			result = append(result, str)
			fmt.Println("EFA", strings.TrimSpace(str))
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func main() {
	var amiURI, command string

	fmt.Println("E1")
	flag.StringVar(&amiURI, "amiuri", "ami://user:pass@host", "AMI uri")
	flag.StringVar(&command, "command", "sip show peers", "command to execute")
	flag.Parse()
	fmt.Println("E2")

	//commandResult, err := executeCommand(amiURI, command)
	err := receiveEvents(amiURI)
	fmt.Println("E3")
	if err != nil {
		log.Panic("Error", err)
		return
	}
	fmt.Println("E4")

}
<commit_msg>Get events as json<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net"
	"strings"
	"time"

	"encoding/json"
	"net/url"

	"github.com/cenkalti/backoff"
)

var (
	host, user, password, endpoint string
	port                           int
	debug                          bool = false
)

func connectToManager(nivis_ami_uri string) (net.Conn, error) {
	fmt.Println("C1")
	u, err := url.Parse(nivis_ami_uri)
	if err != nil {
		return nil, err
	}
	password, _ := u.User.Password()
	user := u.User.Username()
	hostPort := fmt.Sprintf("%s:%d", u.Host, 5038)

	c, err := net.Dial("tcp", hostPort)
	if err != nil {
		return c, err
	}
	fmt.Fprintf(c, "Action: login\r\n")
	fmt.Fprintf(c, "Username: %s\r\n", user)
	fmt.Fprintf(c, "Secret: %s\r\n", password)
	fmt.Fprintf(c, "\r\n")

	return c, err
}

func executeCommand(nivis_ami_uri, command string) ([]string, error) {
	result := []string{}
	c, err := connectToManager(nivis_ami_uri)
	if err != nil {
		return result, err
	}
	defer c.Close()

	fmt.Fprintf(c, "Action: command\r\n")
	fmt.Fprintf(c, "Command: %s\r\n", command)
	fmt.Fprintf(c, "\r\n")

	connbuf := bufio.NewReader(c)
	for {
		str, err := connbuf.ReadString('\n')
		if len(str) > 0 {
			if str == "--END COMMAND--\r\n" {
				break
			}
			result = append(result, str)
		}
		if err != nil {
			return []string{}, err
		}
	}
	return result, nil
}

type Event struct {
	Timestamp int64
	Data      map[string]string
}

func receiveEvents(nivis_ami_uri string, events chan (Event)) error {
	c, err := connectToManager(nivis_ami_uri)
	if err != nil {
		return err
	}
	defer c.Close()

	connbuf := bufio.NewReader(c)
	data := make(map[string]string)
	for {
		str, err := connbuf.ReadString('\n')
		str = strings.TrimSpace(str)
		if len(str) > 0 {
			r := strings.SplitN(str, ":", 2)
			if len(r) == 2 {
				data[r[0]] = r[1]
			}
		} else {
			events <- Event{time.Now().Unix(), data}
			data = make(map[string]string)
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func main() {
	var amiURI, command string

	fmt.Println("E1")
	flag.StringVar(&amiURI, "amiuri", "ami://user:pass@host", "AMI uri")
	flag.StringVar(&command, "command", "sip show peers", "command to execute")
	flag.Parse()
	fmt.Println("E2")

	events := make(chan (Event), 1)

	//commandResult, err := executeCommand(amiURI, command)

	go func() {
		operation := func() error {
			return receiveEvents(amiURI, events)
		}

		err := backoff.Retry(operation, backoff.NewExponentialBackOff())
		if err != nil {
			log.Panic("Error", err)
			return
		}
	}()

	for e := range events {
		jsonSerialized, _ := json.Marshal(e)
		fmt.Println("Event", string(jsonSerialized))
	}

	fmt.Println("E4")

}
<|endoftext|>


<commit_before>package main

import (
	"go/ast"
	"go/parser"
	"testing"
)

func TestIsAssert_Regression(t *testing.T) {
	defer func() {
		if p := recover(); p != nil {
			t.Error(p)
		}
	}()

	exprs, err := parser.ParseExpr(`(&BrowseNode{Path: "/foo/bar/"}).ParentPath()`)
	if err != nil {
		panic(err)
	}

	// Check no panic on isAssert when *ast.SelectorExpr.X is not *ast.Ident
	isAssert(assertImportIdent, exprs.(*ast.CallExpr))
}
<commit_msg>test(gopwt): add tests for createUntypedExprFromBinaryExpr<commit_after>package main

import (
	"github.com/ToQoz/gopwt/assert"
	"go/ast"
	"go/parser"
	"testing"
)

func TestIsAssert_Regression(t *testing.T) {
	defer func() {
		if p := recover(); p != nil {
			t.Error(p)
		}
	}()

	exprs, err := parser.ParseExpr(`(&BrowseNode{Path: "/foo/bar/"}).ParentPath()`)
	if err != nil {
		panic(err)
	}

	// Check no panic on isAssert when *ast.SelectorExpr.X is not *ast.Ident
	isAssert(assertImportIdent, exprs.(*ast.CallExpr))
}

func TestCreateUntypedExprFromBinaryExpr(t *testing.T) {
	var f *ast.CallExpr

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 + 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpADD")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 - 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpSUB")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 * 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpMUL")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 / 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpQUO")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 % 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpREM")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 & 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpAND")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 | 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpOR")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 ^ 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpXOR")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 &^ 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpANDNOT")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 << 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpSHL")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("1 >> 2").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpSHR")
	assert.OK(t, f.Args[0].(*ast.BasicLit).Value == "1")
	assert.OK(t, f.Args[1].(*ast.BasicLit).Value == "2")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("true && true").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpLAND")
	assert.OK(t, f.Args[0].(*ast.Ident).Name == "true")
	assert.OK(t, f.Args[1].(*ast.Ident).Name == "true")

	f = createUntypedExprFromBinaryExpr(mustParseExpr("false || false").(*ast.BinaryExpr)).(*ast.CallExpr)
	assert.OK(t, f.Fun.(*ast.SelectorExpr).Sel.Name == "OpLOR")
	assert.OK(t, f.Args[0].(*ast.Ident).Name == "false")
	assert.OK(t, f.Args[1].(*ast.Ident).Name == "false")
}
<|endoftext|>