<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

import (
	"github.com/jinzhu/gorm"
	"time"
)

// data models
type Match struct {
	gorm.Model

	DataHash string `gorm:"not null,unique"`
	DateTime time.Time
	Duration uint
	Map      string
	Type     string
}

type Player struct {
	gorm.Model

	Name    string
	Aliases []string

	Wins uint
}

type WeaponStat struct {
	gorm.Model

	Hits  uint
	Shots uint
	Kills uint
}
<commit_msg>models/match: use sql tag<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

import (
	"github.com/jinzhu/gorm"
	"time"
)

// data models
type Match struct {
	gorm.Model

	DataHash string `sql:"not null,unique"`
	DateTime time.Time
	Duration uint
	Map      string
	Type     string
}

type Player struct {
	gorm.Model

	Name    string
	Aliases []string

	Wins uint
}

type WeaponStat struct {
	gorm.Model

	Hits  uint
	Shots uint
	Kills uint
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"github.com/google/uuid"
)

// Post struct based on posts table in database
type Post struct {
	ID          uuid.UUID `db:"id"`
	UserID      uuid.UUID `db:"user_id"`
	Title       string    `db:"title"`
	Slug        string    `db:"slug"`
	SubTitle    string    `db:"sub_title"`
	Short       string    `db:"short"`
	PostContent string    `db:"post_content"`
	Digest      string    `db:"digest"`
	Published   bool      `db:"published"`
	UpdatedAt   time.Time `db:"updated_at"`
	CreatedAt   time.Time `db:"created_at"`
}

// Image struct based on image table in database
type Image struct {
	ID        uuid.UUID `db:"id"`
	UserID    uuid.UUID `db:"user_id"`
	URL       string    `db:"url"`
	Medium    string    `db:"medium"`
	Small     string    `db:"small"`
	Caption   string    `db:"caption"`
	UpdatedAt time.Time `db:"updated_at"`
	CreatedAt time.Time `db:"created_at"`
}

// Tag struct based on tag table in database
type Tag struct {
	ID   uuid.UUID `db:"id"`
	Name string    `db:"name"`
	Slug string    `db:"slug"`
}
<commit_msg>add post database functions<commit_after>package models

import (
	"time"

	"github.com/google/uuid"
)

// Post struct based on posts table in database
type Post struct {
	ID          uuid.UUID `db:"id"`
	UserID      uuid.UUID `db:"user_id"`
	Title       string    `db:"title"`
	Slug        string    `db:"slug"`
	SubTitle    string    `db:"sub_title"`
	Short       string    `db:"short"`
	PostContent string    `db:"post_content"`
	Digest      string    `db:"digest"`
	Published   bool      `db:"published"`
	UpdatedAt   time.Time `db:"updated_at"`
	CreatedAt   time.Time `db:"created_at"`
}

// Image struct based on image table in database
type Image struct {
	ID        uuid.UUID `db:"id"`
	UserID    uuid.UUID `db:"user_id"`
	URL       string    `db:"url"`
	Medium    string    `db:"medium"`
	Small     string    `db:"small"`
	Caption   string    `db:"caption"`
	UpdatedAt time.Time `db:"updated_at"`
	CreatedAt time.Time `db:"created_at"`
}

// Tag struct based on tag table in database
type Tag struct {
	ID   uuid.UUID `db:"id"`
	Name string    `db:"name"`
	Slug string    `db:"slug"`
}

// FindPost ...
func (db *DB) FindPost(id uuid.UUID) (*Post, error) {
	p := new(Post)
	sql := "SELECT * FROM posts WHERE id = $1"
	err := db.Get(p, sql, id)
	return p, err
}

// FindPostsByUser ...
func (db *DB) FindPostsByUser(user uuid.UUID) (*[]Post, error) {
	p := new([]Post)
	sql := "SELECT * FROM posts WHERE user_id = $1"
	err := db.Select(p, sql, user)
	return p, err
}

// InsertPost ...
func (db *DB) InsertPost(user uuid.UUID, title string, slug string, subtitle string, short string, content string, digest string, published bool) (*Post, error) {
	p := new(Post)
	sql := "INSERT INTO posts (user_id, title, slug, subtitle, short, content, digest, published) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *"
	err := db.Get(p, sql, user, title, slug, subtitle, short, content, digest, published)
	return p, err
}

// UpdatePost ...
func (db *DB) UpdatePost(id uuid.UUID, user uuid.UUID, title string, slug string, subtitle string, short string, content string, digest string, published bool) (*Post, error) {
	p := new(Post)
	sql := "UPDATE posts SET (user_id, title, slug, subtitle, short, content, digest, published) VALUES ($2, $3, $4, $5, $6, $7, $8) WHERE id = $1 RETURNING *"
	err := db.Get(p, sql, id, user, title, slug, subtitle, short, content, digest, published)
	return p, err
}

// DeletePost ...
func (db *DB) DeletePost(id uuid.UUID) (*Post, error) {
	p := new(Post)
	sql := "DELETE FROM posts WHERE id = $1 RETURNING *"
	err := db.Get(p, sql, id)
	return p, err
}

// FindImage ...
func (db *DB) FindImage(id uuid.UUID) (*Image, error) {

}

// FindImagesByUser
func (db *DB) FindImagesByUser(user uuid.UUID) (*[]Image, error) {

}

// InsertImage
func (db *DB) InsertImage(user uuid.UUID, url string, medium string, small string, caption string) (*Image, error) {

}

//DeleteImage
func (db *DB) DeleteImage(id uuid.UUID) (*Image, error) {

}
<|endoftext|>


<commit_before>package torrent

import "github.com/anacrolix/sync"

// Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that
// would only occur with a write lock at present. The race detector should catch instances of defers
// without the write lock being held.
type lockWithDeferreds struct {
	internal      sync.RWMutex
	unlockActions []func()
}

func (me *lockWithDeferreds) Lock() {
	me.internal.Lock()
}

func (me *lockWithDeferreds) Unlock() {
	for _, a := range me.unlockActions {
		a()
	}
	me.unlockActions = me.unlockActions[:0]
	me.internal.Unlock()
}

func (me *lockWithDeferreds) RLock() {
	me.internal.RLock()
}

func (me *lockWithDeferreds) RUnlock() {
	me.internal.RUnlock()
}

func (me *lockWithDeferreds) Defer(action func()) {
	me.unlockActions = append(me.unlockActions, action)
}
<commit_msg>lockWithDeferreds: inlineable Unlock() (#603)<commit_after>package torrent

import "github.com/anacrolix/sync"

// Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that
// would only occur with a write lock at present. The race detector should catch instances of defers
// without the write lock being held.
type lockWithDeferreds struct {
	internal      sync.RWMutex
	unlockActions []func()
}

func (me *lockWithDeferreds) Lock() {
	me.internal.Lock()
}

func (me *lockWithDeferreds) Unlock() {
	unlockActions := me.unlockActions
	for i := 0; i < len(unlockActions); i += 1 {
		unlockActions[i]()
	}
	me.unlockActions = unlockActions[:0]
	me.internal.Unlock()
}

func (me *lockWithDeferreds) RLock() {
	me.internal.RLock()
}

func (me *lockWithDeferreds) RUnlock() {
	me.internal.RUnlock()
}

func (me *lockWithDeferreds) Defer(action func()) {
	me.unlockActions = append(me.unlockActions, action)
}
<|endoftext|>


<commit_before>package multiplex

import (
	"bufio"
	"io"

	"github.com/pkg/errors"
)

const (
	// TODO: Figure out if we should set this on a per-machine basis. This value
	// is taken from Go's io.Copy method, which defaults to allocating a 32k
	// buffer if none is provided.
	readMultiplexerCopyBufferSize = 32 * 1024
)

// readMultiplexer implements the multiplexing protocol over an io.Reader. It
// uses a polling Goroutine to read headers and then forwards their data blocks
// to io.Pipe objects that are given out as channels.
type readMultiplexer struct {
	pipes []*io.PipeWriter
}

// closeAllPipesWithError closes all pipes in the multiplexer with the specified
// error, letting all channels know that they will not receive any more data. It
// does not close the underlying reader, which must be done to ensure polling
// shutdown.
func (r *readMultiplexer) closeAllPipesWithError(err error) {
	for _, p := range r.pipes {
		p.CloseWithError(err)
	}
}

// poll polls on the underlying reader and forwards data blocks to their
// respective channels.
//
// This function will work correctly on a raw io.Reader, but the interface
// mandates the reader be buffered to avoid unnecessary overhead on short reads
// during header decoding.
//
// Because poll alternates between reading from the underlying reader and
// writing to the various channel pipes, its termination can only be guaranteed
// when the underlying reader and all pipes have been closed. Additionally, the
// underlying reader must unblock reads when closed, but this is definitely not
// true of all readers, so be careful! It is true for io.Pipe streams, net.Conn
// implementations, and some others, but it is very much not true for OS pipes
// on all systems (for OS pipes, the reader will unblock if the write end is
// closed, but not necessarily if the read end is closed (and trying to close
// the read end during a blocking read can even block the close)).
func (r *readMultiplexer) poll(reader *bufio.Reader) {
	// Create a limiting wrapper around the reader to restrict read lengths.
	limiter := &io.LimitedReader{R: reader}

	// Create a copy buffer we can use for data forwarding.
	buffer := make([]byte, readMultiplexerCopyBufferSize)

	// Loop until there's an error.
	for {
		// Read the next header. If that fails, close all pipes.
		header, err := readHeader(reader)
		if err != nil {
			r.closeAllPipesWithError(err)
			return
		}

		// Verify that the channel is valid and extract its pipe.
		if int(header.channel) >= len(r.pipes) {
			r.closeAllPipesWithError(errors.New("received invalid channel"))
			return
		}

		// Forward this channel's data. io.LimitedReader doesn't treat an early
		// io.EOF as an io.ErrUnexpectedEOF (it's a limiting value, not a
		// minimum value), so we also check that the transmitted length is what
		// we expect (because CopyBuffer is expecting an io.EOF as its
		// termination condition and will just gobble that up).
		limiter.N = int64(header.length)
		if copied, err := io.CopyBuffer(r.pipes[header.channel], limiter, buffer); err != nil {
			r.closeAllPipesWithError(err)
			return
		} else if copied != int64(header.length) {
			r.closeAllPipesWithError(io.ErrUnexpectedEOF)
			return
		}
	}
}

// Close closes all pipes in the multiplexer with io.ErrClosedPipe, letting all
// channels know that they will not receive any more data. It does not close the
// underlying reader, which must be done to ensure polling shutdown.
func (r *readMultiplexer) Close() error {
	r.closeAllPipesWithError(nil)
	return nil
}

// Reader demultiplexes an io.Reader into independent byte streams. Multiplexing
// can be accomplished using the Writer method. Demultiplexing will use an
// additional background Goroutine to poll the underlying reader for data and
// route it appropriately. This Goroutine is only guaranteed to terminate when
// both the returned io.Closer is closed (which terminates data routing) and the
// underlying io.Reader is closed and reads on it unblock. This means that the
// io.Reader provided to this method must unblock when closed, and you should be
// aware that this guarantee is not part of the io.Reader interface and that
// many io.Reader objects don't adhere to this behavior. Be particularly careful
// with OS pipes, because on some platforms reads can only be unblocked by
// closing the write end of the pipe, and closes on the read end while in a
// blocking read can even block the close operation.
func Reader(reader io.Reader, channels uint8) ([]io.Reader, io.Closer) {
	// Create our channel pipes.
	readers := make([]io.Reader, channels)
	pipes := make([]*io.PipeWriter, channels)
	for c := uint8(0); c < channels; c++ {
		r, w := io.Pipe()
		readers[c] = r
		pipes[c] = w
	}

	// Create the multiplexer and start its polling Goroutine.
	multiplexer := &readMultiplexer{pipes}
	go multiplexer.poll(bufio.NewReader(reader))

	// Done.
	return readers, multiplexer
}
<commit_msg>Renamed readMultiplexer to readDemultiplexer.<commit_after>package multiplex

import (
	"bufio"
	"io"

	"github.com/pkg/errors"
)

const (
	// TODO: Figure out if we should set this on a per-machine basis. This value
	// is taken from Go's io.Copy method, which defaults to allocating a 32k
	// buffer if none is provided.
	readDemultiplexerCopyBufferSize = 32 * 1024
)

// readDemultiplexer implements the multiplexing protocol over an io.Reader. It
// uses a polling Goroutine to read headers and then forwards their data blocks
// to io.Pipe objects that are given out as channels.
type readDemultiplexer struct {
	pipes []*io.PipeWriter
}

// closeAllPipesWithError closes all pipes in the multiplexer with the specified
// error, letting all channels know that they will not receive any more data. It
// does not close the underlying reader, which must be done to ensure polling
// shutdown.
func (r *readDemultiplexer) closeAllPipesWithError(err error) {
	for _, p := range r.pipes {
		p.CloseWithError(err)
	}
}

// poll polls on the underlying reader and forwards data blocks to their
// respective channels.
//
// This function will work correctly on a raw io.Reader, but the interface
// mandates the reader be buffered to avoid unnecessary overhead on short reads
// during header decoding.
//
// Because poll alternates between reading from the underlying reader and
// writing to the various channel pipes, its termination can only be guaranteed
// when the underlying reader and all pipes have been closed. Additionally, the
// underlying reader must unblock reads when closed, but this is definitely not
// true of all readers, so be careful! It is true for io.Pipe streams, net.Conn
// implementations, and some others, but it is very much not true for OS pipes
// on all systems (for OS pipes, the reader will unblock if the write end is
// closed, but not necessarily if the read end is closed (and trying to close
// the read end during a blocking read can even block the close)).
func (r *readDemultiplexer) poll(reader *bufio.Reader) {
	// Create a limiting wrapper around the reader to restrict read lengths.
	limiter := &io.LimitedReader{R: reader}

	// Create a copy buffer we can use for data forwarding.
	buffer := make([]byte, readDemultiplexerCopyBufferSize)

	// Loop until there's an error.
	for {
		// Read the next header. If that fails, close all pipes.
		header, err := readHeader(reader)
		if err != nil {
			r.closeAllPipesWithError(err)
			return
		}

		// Verify that the channel is valid and extract its pipe.
		if int(header.channel) >= len(r.pipes) {
			r.closeAllPipesWithError(errors.New("received invalid channel"))
			return
		}

		// Forward this channel's data. io.LimitedReader doesn't treat an early
		// io.EOF as an io.ErrUnexpectedEOF (it's a limiting value, not a
		// minimum value), so we also check that the transmitted length is what
		// we expect (because CopyBuffer is expecting an io.EOF as its
		// termination condition and will just gobble that up).
		limiter.N = int64(header.length)
		if copied, err := io.CopyBuffer(r.pipes[header.channel], limiter, buffer); err != nil {
			r.closeAllPipesWithError(err)
			return
		} else if copied != int64(header.length) {
			r.closeAllPipesWithError(io.ErrUnexpectedEOF)
			return
		}
	}
}

// Close closes all pipes in the multiplexer with io.ErrClosedPipe, letting all
// channels know that they will not receive any more data. It does not close the
// underlying reader, which must be done to ensure polling shutdown.
func (r *readDemultiplexer) Close() error {
	r.closeAllPipesWithError(nil)
	return nil
}

// Reader demultiplexes an io.Reader into independent byte streams. Multiplexing
// can be accomplished using the Writer method. Demultiplexing will use an
// additional background Goroutine to poll the underlying reader for data and
// route it appropriately. This Goroutine is only guaranteed to terminate when
// both the returned io.Closer is closed (which terminates data routing) and the
// underlying io.Reader is closed and reads on it unblock. This means that the
// io.Reader provided to this method must unblock when closed, and you should be
// aware that this guarantee is not part of the io.Reader interface and that
// many io.Reader objects don't adhere to this behavior. Be particularly careful
// with OS pipes, because on some platforms reads can only be unblocked by
// closing the write end of the pipe, and closes on the read end while in a
// blocking read can even block the close operation.
func Reader(reader io.Reader, channels uint8) ([]io.Reader, io.Closer) {
	// Create our channel pipes.
	readers := make([]io.Reader, channels)
	pipes := make([]*io.PipeWriter, channels)
	for c := uint8(0); c < channels; c++ {
		r, w := io.Pipe()
		readers[c] = r
		pipes[c] = w
	}

	// Create the multiplexer and start its polling Goroutine.
	multiplexer := &readDemultiplexer{pipes}
	go multiplexer.poll(bufio.NewReader(reader))

	// Done.
	return readers, multiplexer
}
<|endoftext|>


<commit_before>package code_generator

import (
	"github.com/st0012/Rooby/lexer"
	"github.com/st0012/Rooby/parser"
	"strings"
	"testing"
)

func TestCallBlockCompilation(t *testing.T) {
	input := `
def foo
  yield(20, 10)
end

self.foo do |x, y|
  x - y
end
`
	expected := `
<Def:foo>
0 putself
1 putobject 20
2 putobject 10
3 invokeblock 2
4 leave
<Block>
0 getlocal 0
1 getlocal 1
2 send - 1
3 leave
<ProgramStart>
0 putself
1 putstring "foo"
2 def_method 0
3 putself
4 send foo 0 block
5 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestHashCompilation(t *testing.T) {
	input := `
	a = { foo: 1, bar: 5 }
	b = {}
	b["baz"] = a["bar"] - a["foo"]
	b["baz"] + a["bar"]
`

	expected1 := `
<ProgramStart>
0 putstring "foo"
1 putobject 1
2 putstring "bar"
3 putobject 5
4 newhash 4
5 setlocal 0
6 newhash 0
7 setlocal 1
8 getlocal 1
9 putstring "baz"
10 getlocal 0
11 putstring "bar"
12 send [] 1
13 getlocal 0
14 putstring "foo"
15 send [] 1
16 send - 1
17 send []= 2
18 getlocal 1
19 putstring "baz"
20 send [] 1
21 getlocal 0
22 putstring "bar"
23 send [] 1
24 send + 1
25 leave
`
	expected2 := `
<ProgramStart>
0 putstring "bar"
1 putobject 5
2 putstring "foo"
3 putobject 1
4 newhash 4
5 setlocal 0
6 newhash 0
7 setlocal 1
8 getlocal 1
9 putstring "baz"
10 getlocal 0
11 putstring "bar"
12 send [] 1
13 getlocal 0
14 putstring "foo"
15 send [] 1
16 send - 1
17 send []= 2
18 getlocal 1
19 putstring "baz"
20 send [] 1
21 getlocal 0
22 putstring "bar"
23 send [] 1
24 send + 1
25 leave
`
	bytecode := strings.TrimSpace(compileToBytecode(input))

	// This is because hash stores data using map.
	// And map's keys won't be sorted when running in for loop.
	// So we can get 2 possible results.
	expected1 = strings.TrimSpace(expected1)
	expected2 = strings.TrimSpace(expected2)
	if bytecode != expected1 && bytecode != expected2 {
		t.Fatalf(`
Bytecode compare failed
Expect:
"%s"

Or:

"%s"

Got:
"%s"
`, expected1, expected2, bytecode)
	}

}

func TestArrayCompilation(t *testing.T) {
	input := `
	a = [1, 2, "bar"]
	a[0] = "foo"
	c = a[0]
`

	expected := `
<ProgramStart>
0 putobject 1
1 putobject 2
2 putstring "bar"
3 newarray 3
4 setlocal 0
5 getlocal 0
6 putobject 0
7 putstring "foo"
8 send []= 2
9 getlocal 0
10 putobject 0
11 send [] 1
12 setlocal 1
13 leave
`
	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestCumstomConstructor(t *testing.T) {
	input := `
class Foo
  def initialize(x, y)
    @x = x
    @y = y
    @z = x - y
  end

  def bar
    @x + @y + @z
  end
end

Foo.new(100, 50).bar
`

	expected := `
<Def:initialize>
0 getlocal 0
1 setinstancevariable @x
2 getlocal 1
3 setinstancevariable @y
4 getlocal 0
5 getlocal 1
6 send - 1
7 setinstancevariable @z
8 leave
<Def:bar>
0 getinstancevariable @x
1 getinstancevariable @y
2 send + 1
3 getinstancevariable @z
4 send + 1
5 leave
<DefClass:Foo>
0 putself
1 putstring "initialize"
2 def_method 2
3 putself
4 putstring "bar"
5 def_method 0
6 leave
<ProgramStart>
0 putself
1 def_class Foo
2 pop
3 getconstant Foo
4 putobject 100
5 putobject 50
6 send new 2
7 send bar 0
8 leave
`
	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestClassMethodDefinition(t *testing.T) {
	input := `
class Foo
  def self.bar
    10
  end
end

Foo.bar
`
	expected := `
<Def:bar>
0 putobject 10
1 leave
<DefClass:Foo>
0 putself
1 putstring "bar"
2 def_singleton_method 0
3 leave
<ProgramStart>
0 putself
1 def_class Foo
2 pop
3 getconstant Foo
4 send bar 0
5 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestClassDefinition(t *testing.T) {
	input := `
class Bar
  def bar
    10
  end
end

class Foo < Bar
end

Foo.new.bar
`
	expected := `
<Def:bar>
0 putobject 10
1 leave
<DefClass:Bar>
0 putself
1 putstring "bar"
2 def_method 0
3 leave
<DefClass:Foo>
0 leave
<ProgramStart>
0 putself
1 def_class Bar
2 pop
3 putself
4 def_class Foo Bar
5 pop
6 getconstant Foo
7 send new 0
8 send bar 0
9 leave
`
	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestBasicMethodReDefineAndExecution(t *testing.T) {
	input := `
	def foo(x)
	  x + 100
	end

	def foo(x)
	  x + 10
	end

	foo(11)
	`

	expected := `
<Def:foo>
0 getlocal 0
1 putobject 100
2 send + 1
3 leave
<Def:foo>
0 getlocal 0
1 putobject 10
2 send + 1
3 leave
<ProgramStart>
0 putself
1 putstring "foo"
2 def_method 1
3 putself
4 putstring "foo"
5 def_method 1
6 putself
7 putobject 11
8 send foo 1
9 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestBasicMethodDefineAndExecution(t *testing.T) {
	input := `
	def foo(x, y)
	  z = 10
	  x - y + z
	end

	foo(11, 1)
	`

	expected := `
<Def:foo>
0 putobject 10
1 setlocal 2
2 getlocal 0
3 getlocal 1
4 send - 1
5 getlocal 2
6 send + 1
7 leave
<ProgramStart>
0 putself
1 putstring "foo"
2 def_method 2
3 putself
4 putobject 11
5 putobject 1
6 send foo 2
7 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestArithmeticCompilation(t *testing.T) {
	input := `
	(1 * 10 + 100) / 2
	`

	expected := `
<ProgramStart>
0 putobject 1
1 putobject 10
2 send * 1
3 putobject 100
4 send + 1
5 putobject 2
6 send / 1
7 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestLocalVariableAccessInCurrentScope(t *testing.T) {
	input := `
	a = 10
	a = 100
	b = 5
	(b * a + 100) / 2
	`
	expected := `
<ProgramStart>
0 putobject 10
1 setlocal 0
2 putobject 100
3 setlocal 0
4 putobject 5
5 setlocal 1
6 getlocal 1
7 getlocal 0
8 send * 1
9 putobject 100
10 send + 1
11 putobject 2
12 send / 1
13 leave`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestConditionWithoutAlternativeCompilation(t *testing.T) {
	input := `
	a = 10
	b = 5
	if a > b
	  c = 10
	end

	c + 1
	`

	expected := `
<ProgramStart>
0 putobject 10
1 setlocal 0
2 putobject 5
3 setlocal 1
4 getlocal 0
5 getlocal 1
6 send > 1
7 branchunless 10
8 putobject 10
9 setlocal 2
10 putnil
11 getlocal 2
12 putobject 1
13 send + 1
14 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestConditionWithAlternativeCompilation(t *testing.T) {
	input := `
	a = 10
	b = 5
	if a > b
	  c = 10
	else
	  c = 5
	end

	c + 1
	`

	expected := `
<ProgramStart>
0 putobject 10
1 setlocal 0
2 putobject 5
3 setlocal 1
4 getlocal 0
5 getlocal 1
6 send > 1
7 branchunless 11
8 putobject 10
9 setlocal 2
10 jump 13
11 putobject 5
12 setlocal 2
13 getlocal 2
14 putobject 1
15 send + 1
16 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func compileToBytecode(input string) string {
	l := lexer.New(input)
	p := parser.New(l)
	program := p.ParseProgram()
	p.CheckErrors()
	cg := New(program)
	return cg.GenerateByteCode(program)
}

func compareBytecode(t *testing.T, value, expected string) {
	value = strings.TrimSpace(value)
	expected = strings.TrimSpace(expected)
	if value != expected {
		t.Fatalf(`
Bytecode compare failed
Expect:
"%s"

Got:
"%s"
`, expected, value)
	}
}
<commit_msg>Update code_generator's test.<commit_after>package code_generator

import (
	"github.com/st0012/Rooby/lexer"
	"github.com/st0012/Rooby/parser"
	"strings"
	"testing"
)

func TestCallBlockCompilation(t *testing.T) {
	input := `
def foo
  yield(20, 10)
end

self.foo do |x, y|
  x - y
end
`
	expected := `
<Def:foo>
0 putself
1 putobject 20
2 putobject 10
3 invokeblock 2
4 leave
<Block>
0 getlocal 0
1 getlocal 1
2 send - 1
3 leave
<ProgramStart>
0 putself
1 putstring "foo"
2 def_method 0
3 putself
4 send foo 0 true
5 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestHashCompilation(t *testing.T) {
	input := `
	a = { foo: 1, bar: 5 }
	b = {}
	b["baz"] = a["bar"] - a["foo"]
	b["baz"] + a["bar"]
`

	expected1 := `
<ProgramStart>
0 putstring "foo"
1 putobject 1
2 putstring "bar"
3 putobject 5
4 newhash 4
5 setlocal 0
6 newhash 0
7 setlocal 1
8 getlocal 1
9 putstring "baz"
10 getlocal 0
11 putstring "bar"
12 send [] 1
13 getlocal 0
14 putstring "foo"
15 send [] 1
16 send - 1
17 send []= 2
18 getlocal 1
19 putstring "baz"
20 send [] 1
21 getlocal 0
22 putstring "bar"
23 send [] 1
24 send + 1
25 leave
`
	expected2 := `
<ProgramStart>
0 putstring "bar"
1 putobject 5
2 putstring "foo"
3 putobject 1
4 newhash 4
5 setlocal 0
6 newhash 0
7 setlocal 1
8 getlocal 1
9 putstring "baz"
10 getlocal 0
11 putstring "bar"
12 send [] 1
13 getlocal 0
14 putstring "foo"
15 send [] 1
16 send - 1
17 send []= 2
18 getlocal 1
19 putstring "baz"
20 send [] 1
21 getlocal 0
22 putstring "bar"
23 send [] 1
24 send + 1
25 leave
`
	bytecode := strings.TrimSpace(compileToBytecode(input))

	// This is because hash stores data using map.
	// And map's keys won't be sorted when running in for loop.
	// So we can get 2 possible results.
	expected1 = strings.TrimSpace(expected1)
	expected2 = strings.TrimSpace(expected2)
	if bytecode != expected1 && bytecode != expected2 {
		t.Fatalf(`
Bytecode compare failed
Expect:
"%s"

Or:

"%s"

Got:
"%s"
`, expected1, expected2, bytecode)
	}

}

func TestArrayCompilation(t *testing.T) {
	input := `
	a = [1, 2, "bar"]
	a[0] = "foo"
	c = a[0]
`

	expected := `
<ProgramStart>
0 putobject 1
1 putobject 2
2 putstring "bar"
3 newarray 3
4 setlocal 0
5 getlocal 0
6 putobject 0
7 putstring "foo"
8 send []= 2
9 getlocal 0
10 putobject 0
11 send [] 1
12 setlocal 1
13 leave
`
	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestCumstomConstructor(t *testing.T) {
	input := `
class Foo
  def initialize(x, y)
    @x = x
    @y = y
    @z = x - y
  end

  def bar
    @x + @y + @z
  end
end

Foo.new(100, 50).bar
`

	expected := `
<Def:initialize>
0 getlocal 0
1 setinstancevariable @x
2 getlocal 1
3 setinstancevariable @y
4 getlocal 0
5 getlocal 1
6 send - 1
7 setinstancevariable @z
8 leave
<Def:bar>
0 getinstancevariable @x
1 getinstancevariable @y
2 send + 1
3 getinstancevariable @z
4 send + 1
5 leave
<DefClass:Foo>
0 putself
1 putstring "initialize"
2 def_method 2
3 putself
4 putstring "bar"
5 def_method 0
6 leave
<ProgramStart>
0 putself
1 def_class Foo
2 pop
3 getconstant Foo
4 putobject 100
5 putobject 50
6 send new 2
7 send bar 0
8 leave
`
	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestClassMethodDefinition(t *testing.T) {
	input := `
class Foo
  def self.bar
    10
  end
end

Foo.bar
`
	expected := `
<Def:bar>
0 putobject 10
1 leave
<DefClass:Foo>
0 putself
1 putstring "bar"
2 def_singleton_method 0
3 leave
<ProgramStart>
0 putself
1 def_class Foo
2 pop
3 getconstant Foo
4 send bar 0
5 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestClassDefinition(t *testing.T) {
	input := `
class Bar
  def bar
    10
  end
end

class Foo < Bar
end

Foo.new.bar
`
	expected := `
<Def:bar>
0 putobject 10
1 leave
<DefClass:Bar>
0 putself
1 putstring "bar"
2 def_method 0
3 leave
<DefClass:Foo>
0 leave
<ProgramStart>
0 putself
1 def_class Bar
2 pop
3 putself
4 def_class Foo Bar
5 pop
6 getconstant Foo
7 send new 0
8 send bar 0
9 leave
`
	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestBasicMethodReDefineAndExecution(t *testing.T) {
	input := `
	def foo(x)
	  x + 100
	end

	def foo(x)
	  x + 10
	end

	foo(11)
	`

	expected := `
<Def:foo>
0 getlocal 0
1 putobject 100
2 send + 1
3 leave
<Def:foo>
0 getlocal 0
1 putobject 10
2 send + 1
3 leave
<ProgramStart>
0 putself
1 putstring "foo"
2 def_method 1
3 putself
4 putstring "foo"
5 def_method 1
6 putself
7 putobject 11
8 send foo 1
9 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestBasicMethodDefineAndExecution(t *testing.T) {
	input := `
	def foo(x, y)
	  z = 10
	  x - y + z
	end

	foo(11, 1)
	`

	expected := `
<Def:foo>
0 putobject 10
1 setlocal 2
2 getlocal 0
3 getlocal 1
4 send - 1
5 getlocal 2
6 send + 1
7 leave
<ProgramStart>
0 putself
1 putstring "foo"
2 def_method 2
3 putself
4 putobject 11
5 putobject 1
6 send foo 2
7 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestArithmeticCompilation(t *testing.T) {
	input := `
	(1 * 10 + 100) / 2
	`

	expected := `
<ProgramStart>
0 putobject 1
1 putobject 10
2 send * 1
3 putobject 100
4 send + 1
5 putobject 2
6 send / 1
7 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestLocalVariableAccessInCurrentScope(t *testing.T) {
	input := `
	a = 10
	a = 100
	b = 5
	(b * a + 100) / 2
	`
	expected := `
<ProgramStart>
0 putobject 10
1 setlocal 0
2 putobject 100
3 setlocal 0
4 putobject 5
5 setlocal 1
6 getlocal 1
7 getlocal 0
8 send * 1
9 putobject 100
10 send + 1
11 putobject 2
12 send / 1
13 leave`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestConditionWithoutAlternativeCompilation(t *testing.T) {
	input := `
	a = 10
	b = 5
	if a > b
	  c = 10
	end

	c + 1
	`

	expected := `
<ProgramStart>
0 putobject 10
1 setlocal 0
2 putobject 5
3 setlocal 1
4 getlocal 0
5 getlocal 1
6 send > 1
7 branchunless 10
8 putobject 10
9 setlocal 2
10 putnil
11 getlocal 2
12 putobject 1
13 send + 1
14 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func TestConditionWithAlternativeCompilation(t *testing.T) {
	input := `
	a = 10
	b = 5
	if a > b
	  c = 10
	else
	  c = 5
	end

	c + 1
	`

	expected := `
<ProgramStart>
0 putobject 10
1 setlocal 0
2 putobject 5
3 setlocal 1
4 getlocal 0
5 getlocal 1
6 send > 1
7 branchunless 11
8 putobject 10
9 setlocal 2
10 jump 13
11 putobject 5
12 setlocal 2
13 getlocal 2
14 putobject 1
15 send + 1
16 leave
`

	bytecode := compileToBytecode(input)
	compareBytecode(t, bytecode, expected)
}

func compileToBytecode(input string) string {
	l := lexer.New(input)
	p := parser.New(l)
	program := p.ParseProgram()
	p.CheckErrors()
	cg := New(program)
	return cg.GenerateByteCode(program)
}

func compareBytecode(t *testing.T, value, expected string) {
	value = strings.TrimSpace(value)
	expected = strings.TrimSpace(expected)
	if value != expected {
		t.Fatalf(`
Bytecode compare failed
Expect:
"%s"

Got:
"%s"
`, expected, value)
	}
}
<|endoftext|>


<commit_before>package scaling_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"lib/models"
	"lib/policy_client"
	"lib/testsupport"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"code.cloudfoundry.org/lager/lagertest"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf-experimental/rainmaker"
)

const Timeout_Check = 20 * time.Minute

// 3 * observed agent total poll time with 200 containers per cell
const Policy_Update_Wait = 180 * time.Second

var ports []int

var _ = Describe("connectivity between containers on the overlay network", func() {
	Describe("networking policy", func() {
		var (
			appProxy       string
			appRegistry    string
			appsTest       []string
			appInstances   int
			applications   int
			proxyInstances int
			sampleSize     int
		)

		BeforeEach(func() {
			appInstances = pushConfig.AppInstances
			applications = pushConfig.Applications
			proxyInstances = pushConfig.ProxyInstances
			sampleSize = pushConfig.SampleSize
			appProxy = pushConfig.Prefix + "proxy"
			appRegistry = pushConfig.Prefix + "registry"
			for i := 0; i < applications; i++ {
				appsTest = append(appsTest, fmt.Sprintf(pushConfig.Prefix+"tick-%d", i))
			}

			ports = []int{8080}
			for i := 0; i < pushConfig.ExtraListenPorts; i++ {
				ports = append(ports, 7000+i)
			}
		})

		It("allows the user to configure policies", func(done Done) {
			By("checking that all test app instances have registered themselves")
			checkRegistry(appRegistry, 10*time.Second, 500*time.Millisecond, len(appsTest)*appInstances)

			appIPs := getAppIPs(appRegistry)
			sample := sampleIPs(appIPs, sampleSize)

			By(fmt.Sprintf("checking that the connection fails sampling %d out of %d IPs on %d ports", len(sample), len(appIPs), len(ports)))
			runWithTimeout("check connection failures", Timeout_Check, func() {
				assertConnectionFails(appProxy, sample, ports, proxyInstances)
			})

			By("creating policies")
			doAllPolicies("create", appProxy, appsTest, ports)

			By(fmt.Sprintf("waiting %s for policies to be updated on cells", Policy_Update_Wait))
			time.Sleep(Policy_Update_Wait)

			sample = sampleIPs(appIPs, sampleSize)
			By(fmt.Sprintf("checking that the connection succeeds sampling %d out of %d IPs on %d ports", len(sample), len(appIPs), len(ports)))
			runWithTimeout("check connection success", Timeout_Check, func() {
				assertConnectionSucceeds(appProxy, sample, ports, proxyInstances)
			})

			By("dumping stats to commit to stats repo")
			dumpStats(appProxy, config.AppsDomain)

			By("sleeping for 5 minutes while policies exist")
			time.Sleep(5 * time.Minute)

			By("deleting policies")
			doAllPolicies("delete", appProxy, appsTest, ports)

			By(fmt.Sprintf("waiting %s for policies to be updated on cells", Policy_Update_Wait))
			time.Sleep(Policy_Update_Wait)

			sample = sampleIPs(appIPs, sampleSize)
			By(fmt.Sprintf("checking that the connection succeeds sampling %d out of %d IPs on %d ports", len(sample), len(appIPs), len(ports)))
			runWithTimeout("check connection failures, again", Timeout_Check, func() {
				assertConnectionFails(appProxy, sample, ports, proxyInstances)
			})

			close(done)
		}, 30*60 /* <-- overall spec timeout in seconds */)
	})
	Describe("sampleIPs", func() {
		var population []string
		BeforeEach(func() {
			population = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
		})

		It("returns a sample of unique choices from the population", func() {
			sample := sampleIPs(population, 9)
			Expect(len(sample)).To(Equal(9))
			for i := 0; i < len(sample); i++ {
				for j := i + 1; j < len(sample); j++ {
					Expect(sample[i]).NotTo(Equal(sample[j]))
				}
			}
		})
		Context("when the sample size is larger than the population", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, 999)
				Expect(sample).To(Equal(population))
			})
		})
		Context("when the sample size is zero", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, 0)
				Expect(sample).To(Equal(population))
			})
		})
		Context("when the sample size is negative", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, -1)
				Expect(sample).To(Equal(population))
			})
		})
	})
})

func getToken() string {
	cmd := exec.Command("cf", "oauth-token")
	session, err := gexec.Start(cmd, nil, nil)
	Expect(err).NotTo(HaveOccurred())
	Eventually(session.Wait(Timeout_Short)).Should(gexec.Exit(0))
	rawOutput := string(session.Out.Contents())
	return strings.TrimSpace(strings.TrimPrefix(rawOutput, "bearer "))
}

func getGuids(sourceAppName string, dstAppNames []string) (string, []string) {
	dstGuids := []string{}
	sourceGuid := ""
	token := getToken()
	appsClient := rainmaker.NewApplicationsService(rainmaker.Config{Host: "http://" + config.ApiEndpoint})

	appsList, err := appsClient.List(token)
	Expect(err).NotTo(HaveOccurred())

	for {
		for _, app := range appsList.Applications {
			if app.Name == sourceAppName {
				sourceGuid = app.GUID
				continue
			}
			for _, tickAppName := range dstAppNames {
				if app.Name == tickAppName {
					dstGuids = append(dstGuids, app.GUID)
					break
				}
			}
		}
		if appsList.HasNextPage() {
			appsList, err = appsList.Next(token)
			Expect(err).NotTo(HaveOccurred())
		} else {
			break
		}
	}

	Expect(sourceGuid).NotTo(BeEmpty())
	Expect(dstGuids).To(HaveLen(len(dstAppNames)))

	return sourceGuid, dstGuids
}

func doAllPolicies(action string, source string, dstList []string, dstPorts []int) {
	policyClient := policy_client.NewExternal(lagertest.NewTestLogger("test"), &http.Client{}, "http://"+config.ApiEndpoint)
	sourceGuid, dstGuids := getGuids(source, dstList)
	policies := []models.Policy{}
	for _, dstGuid := range dstGuids {
		for _, port := range dstPorts {
			policies = append(policies, models.Policy{
				Source: models.Source{
					ID: sourceGuid,
				},
				Destination: models.Destination{
					ID:       dstGuid,
					Port:     port,
					Protocol: "tcp",
				},
			})
		}
	}
	token := getToken()
	if action == "create" {
		Expect(policyClient.AddPolicies(token, policies)).To(Succeed())
	} else if action == "delete" {
		Expect(policyClient.DeletePolicies(token, policies)).To(Succeed())
	}
}

func runWithTimeout(operation string, timeout time.Duration, work func()) {
	done := make(chan bool)
	go func() {
		By(fmt.Sprintf("starting %s\n", operation))
		work()
		done <- true
	}()

	select {
	case <-done:
		By(fmt.Sprintf("completed %s\n", operation))
		return
	case <-time.After(timeout):
		Fail("timeout on " + operation)
	}
}

func dumpStats(host, domain string) {
	resp, err := httpGetBytes(fmt.Sprintf("http://%s.%s/stats", host, domain))
	Expect(err).NotTo(HaveOccurred())

	netStatsFile := os.Getenv("NETWORK_STATS_FILE")
	if netStatsFile != "" {
		Expect(ioutil.WriteFile(netStatsFile, resp.Body, 0600)).To(Succeed())
	}
}

type RegistryInstancesResponse struct {
	Instances []struct {
		ServiceName string `json:"service_name"`
		Endpoint    struct {
			Value string `json:"value"`
		} `json:"endpoint"`
	} `json:"instances"`
}

func getInstancesFromA8(registry string) (*RegistryInstancesResponse, error) {
	resp, err := httpGetBytes(fmt.Sprintf("http://%s.%s/api/v1/instances", registry, config.AppsDomain))
	if err != nil {
		return nil, err
	}

	Expect(resp.StatusCode).To(Equal(http.StatusOK))

	var instancesResponse RegistryInstancesResponse
	err = json.Unmarshal(resp.Body, &instancesResponse)
	if err != nil {
		return nil, err
	}
	return &instancesResponse, nil
}

func checkRegistry(registry string, timeout, pollingInterval time.Duration, totalInstances int) {
	registeredApps := func() (int, error) {
		instancesResponse, err := getInstancesFromA8(registry)
		if err != nil {
			return 0, err
		}
		return len(instancesResponse.Instances), nil
	}

	Eventually(registeredApps, timeout, pollingInterval).Should(Equal(totalInstances))
}

func getAppIPs(registry string) []string {
	instancesResponse, err := getInstancesFromA8(registry)
	Expect(err).NotTo(HaveOccurred())

	ips := []string{}
	for _, instance := range instancesResponse.Instances {
		ip, _, err := net.SplitHostPort(instance.Endpoint.Value)
		Expect(err).NotTo(HaveOccurred())
		ips = append(ips, ip)
	}
	return ips
}

func sampleIPs(population []string, sampleSize int) []string {
	populationSize := len(population)
	if len(population) < sampleSize || sampleSize < 1 {
		return population
	}
	var sample = []string{}
	for i := 0; i < sampleSize; i++ {
		j := rand.Intn(populationSize)
		sample = append(sample, population[j])
		population = append(population[:j], population[j+1:]...)
		populationSize--
	}
	return sample
}

func assertConnectionSucceeds(sourceApp string, destApps []string, ports []int, nProxies int) {
	parallelRunner := &testsupport.ParallelRunner{
		NumWorkers: 50 * nProxies,
	}
	parallelRunner.RunOnSliceStrings(destApps, func(appIP string) {
		for _, port := range ports {
			assertSingleConnection(appIP, port, sourceApp, true)
		}
	})
}

func assertConnectionFails(sourceApp string, destApps []string, ports []int, nProxies int) {
	parallelRunner := &testsupport.ParallelRunner{
		NumWorkers: 50 * nProxies,
	}
	parallelRunner.RunOnSliceStrings(destApps, func(appIP string) {
		for _, port := range ports {
			assertSingleConnection(appIP, port, sourceApp, false)
		}
	})
}

func assertSingleConnection(destIP string, port int, sourceAppName string, shouldSucceed bool) {
	if shouldSucceed {
		assertResponseContains(destIP, port, sourceAppName, "application_name")
	} else {
		assertResponseContains(destIP, port, sourceAppName, "request failed")
	}
}

func assertResponseContains(destIP string, port int, sourceAppName string, desiredResponse string) {
	proxyTest := func() (string, error) {
		resp, err := httpGetBytes(fmt.Sprintf("http://%s.%s/proxy/%s:%d", sourceAppName, config.AppsDomain, destIP, port))
		if err != nil {
			return "", err
		}
		return string(resp.Body), nil
	}
	Eventually(proxyTest, 10*time.Second, 500*time.Millisecond).Should(ContainSubstring(desiredResponse))
}

var httpClient = &http.Client{
	Transport: &http.Transport{
		DisableKeepAlives: true,
		Dial: (&net.Dialer{
			Timeout:   4 * time.Second,
			KeepAlive: 0,
		}).Dial,
	},
}

type httpResp struct {
	StatusCode int
	Body       []byte
}

func httpGetBytes(url string) (httpResp, error) {
	resp, err := httpClient.Get(url)
	if err != nil {
		return httpResp{}, err
	}
	defer resp.Body.Close()

	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return httpResp{}, err
	}

	return httpResp{resp.StatusCode, respBytes}, nil
}
<commit_msg>Use full population when sample size is equal to population size<commit_after>package scaling_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"lib/models"
	"lib/policy_client"
	"lib/testsupport"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"code.cloudfoundry.org/lager/lagertest"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf-experimental/rainmaker"
)

const Timeout_Check = 20 * time.Minute

// 3 * observed agent total poll time with 200 containers per cell
const Policy_Update_Wait = 180 * time.Second

var ports []int

var _ = Describe("connectivity between containers on the overlay network", func() {
	Describe("networking policy", func() {
		var (
			appProxy       string
			appRegistry    string
			appsTest       []string
			appInstances   int
			applications   int
			proxyInstances int
			sampleSize     int
		)

		BeforeEach(func() {
			appInstances = pushConfig.AppInstances
			applications = pushConfig.Applications
			proxyInstances = pushConfig.ProxyInstances
			sampleSize = pushConfig.SampleSize
			appProxy = pushConfig.Prefix + "proxy"
			appRegistry = pushConfig.Prefix + "registry"
			for i := 0; i < applications; i++ {
				appsTest = append(appsTest, fmt.Sprintf(pushConfig.Prefix+"tick-%d", i))
			}

			ports = []int{8080}
			for i := 0; i < pushConfig.ExtraListenPorts; i++ {
				ports = append(ports, 7000+i)
			}
		})

		It("allows the user to configure policies", func(done Done) {
			By("checking that all test app instances have registered themselves")
			checkRegistry(appRegistry, 10*time.Second, 500*time.Millisecond, len(appsTest)*appInstances)

			appIPs := getAppIPs(appRegistry)
			sample := sampleIPs(appIPs, sampleSize)

			By(fmt.Sprintf("checking that the connection fails sampling %d out of %d IPs on %d ports", len(sample), len(appIPs), len(ports)))
			runWithTimeout("check connection failures", Timeout_Check, func() {
				assertConnectionFails(appProxy, sample, ports, proxyInstances)
			})

			By("creating policies")
			doAllPolicies("create", appProxy, appsTest, ports)

			By(fmt.Sprintf("waiting %s for policies to be updated on cells", Policy_Update_Wait))
			time.Sleep(Policy_Update_Wait)

			sample = sampleIPs(appIPs, sampleSize)
			By(fmt.Sprintf("checking that the connection succeeds sampling %d out of %d IPs on %d ports", len(sample), len(appIPs), len(ports)))
			runWithTimeout("check connection success", Timeout_Check, func() {
				assertConnectionSucceeds(appProxy, sample, ports, proxyInstances)
			})

			By("dumping stats to commit to stats repo")
			dumpStats(appProxy, config.AppsDomain)

			By("sleeping for 5 minutes while policies exist")
			time.Sleep(5 * time.Minute)

			By("deleting policies")
			doAllPolicies("delete", appProxy, appsTest, ports)

			By(fmt.Sprintf("waiting %s for policies to be updated on cells", Policy_Update_Wait))
			time.Sleep(Policy_Update_Wait)

			sample = sampleIPs(appIPs, sampleSize)
			By(fmt.Sprintf("checking that the connection succeeds sampling %d out of %d IPs on %d ports", len(sample), len(appIPs), len(ports)))
			runWithTimeout("check connection failures, again", Timeout_Check, func() {
				assertConnectionFails(appProxy, sample, ports, proxyInstances)
			})

			close(done)
		}, 30*60 /* <-- overall spec timeout in seconds */)
	})
	Describe("sampleIPs", func() {
		var population []string
		BeforeEach(func() {
			population = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
		})

		It("returns a sample of unique choices from the population", func() {
			sample := sampleIPs(population, 9)
			Expect(len(sample)).To(Equal(9))
			for i := 0; i < len(sample); i++ {
				for j := i + 1; j < len(sample); j++ {
					Expect(sample[i]).NotTo(Equal(sample[j]))
				}
			}
		})
		Context("when the sample size is larger than the population", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, 999)
				Expect(sample).To(Equal(population))
			})
		})
		Context("when the sample size is equal to the population size", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, len(population))
				Expect(sample).To(Equal(population))
			})
		})
		Context("when the sample size is zero", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, 0)
				Expect(sample).To(Equal(population))
			})
		})
		Context("when the sample size is negative", func() {
			It("returns the whole population", func() {
				sample := sampleIPs(population, -1)
				Expect(sample).To(Equal(population))
			})
		})
	})
})

func getToken() string {
	cmd := exec.Command("cf", "oauth-token")
	session, err := gexec.Start(cmd, nil, nil)
	Expect(err).NotTo(HaveOccurred())
	Eventually(session.Wait(Timeout_Short)).Should(gexec.Exit(0))
	rawOutput := string(session.Out.Contents())
	return strings.TrimSpace(strings.TrimPrefix(rawOutput, "bearer "))
}

func getGuids(sourceAppName string, dstAppNames []string) (string, []string) {
	dstGuids := []string{}
	sourceGuid := ""
	token := getToken()
	appsClient := rainmaker.NewApplicationsService(rainmaker.Config{Host: "http://" + config.ApiEndpoint})

	appsList, err := appsClient.List(token)
	Expect(err).NotTo(HaveOccurred())

	for {
		for _, app := range appsList.Applications {
			if app.Name == sourceAppName {
				sourceGuid = app.GUID
				continue
			}
			for _, tickAppName := range dstAppNames {
				if app.Name == tickAppName {
					dstGuids = append(dstGuids, app.GUID)
					break
				}
			}
		}
		if appsList.HasNextPage() {
			appsList, err = appsList.Next(token)
			Expect(err).NotTo(HaveOccurred())
		} else {
			break
		}
	}

	Expect(sourceGuid).NotTo(BeEmpty())
	Expect(dstGuids).To(HaveLen(len(dstAppNames)))

	return sourceGuid, dstGuids
}

func doAllPolicies(action string, source string, dstList []string, dstPorts []int) {
	policyClient := policy_client.NewExternal(lagertest.NewTestLogger("test"), &http.Client{}, "http://"+config.ApiEndpoint)
	sourceGuid, dstGuids := getGuids(source, dstList)
	policies := []models.Policy{}
	for _, dstGuid := range dstGuids {
		for _, port := range dstPorts {
			policies = append(policies, models.Policy{
				Source: models.Source{
					ID: sourceGuid,
				},
				Destination: models.Destination{
					ID:       dstGuid,
					Port:     port,
					Protocol: "tcp",
				},
			})
		}
	}
	token := getToken()
	if action == "create" {
		Expect(policyClient.AddPolicies(token, policies)).To(Succeed())
	} else if action == "delete" {
		Expect(policyClient.DeletePolicies(token, policies)).To(Succeed())
	}
}

func runWithTimeout(operation string, timeout time.Duration, work func()) {
	done := make(chan bool)
	go func() {
		By(fmt.Sprintf("starting %s\n", operation))
		work()
		done <- true
	}()

	select {
	case <-done:
		By(fmt.Sprintf("completed %s\n", operation))
		return
	case <-time.After(timeout):
		Fail("timeout on " + operation)
	}
}

func dumpStats(host, domain string) {
	resp, err := httpGetBytes(fmt.Sprintf("http://%s.%s/stats", host, domain))
	Expect(err).NotTo(HaveOccurred())

	netStatsFile := os.Getenv("NETWORK_STATS_FILE")
	if netStatsFile != "" {
		Expect(ioutil.WriteFile(netStatsFile, resp.Body, 0600)).To(Succeed())
	}
}

type RegistryInstancesResponse struct {
	Instances []struct {
		ServiceName string `json:"service_name"`
		Endpoint    struct {
			Value string `json:"value"`
		} `json:"endpoint"`
	} `json:"instances"`
}

func getInstancesFromA8(registry string) (*RegistryInstancesResponse, error) {
	resp, err := httpGetBytes(fmt.Sprintf("http://%s.%s/api/v1/instances", registry, config.AppsDomain))
	if err != nil {
		return nil, err
	}

	Expect(resp.StatusCode).To(Equal(http.StatusOK))

	var instancesResponse RegistryInstancesResponse
	err = json.Unmarshal(resp.Body, &instancesResponse)
	if err != nil {
		return nil, err
	}
	return &instancesResponse, nil
}

func checkRegistry(registry string, timeout, pollingInterval time.Duration, totalInstances int) {
	registeredApps := func() (int, error) {
		instancesResponse, err := getInstancesFromA8(registry)
		if err != nil {
			return 0, err
		}
		return len(instancesResponse.Instances), nil
	}

	Eventually(registeredApps, timeout, pollingInterval).Should(Equal(totalInstances))
}

func getAppIPs(registry string) []string {
	instancesResponse, err := getInstancesFromA8(registry)
	Expect(err).NotTo(HaveOccurred())

	ips := []string{}
	for _, instance := range instancesResponse.Instances {
		ip, _, err := net.SplitHostPort(instance.Endpoint.Value)
		Expect(err).NotTo(HaveOccurred())
		ips = append(ips, ip)
	}
	return ips
}

func sampleIPs(population []string, sampleSize int) []string {
	populationSize := len(population)
	if len(population) <= sampleSize || sampleSize < 1 {
		return population
	}
	var sample = []string{}
	for i := 0; i < sampleSize; i++ {
		j := rand.Intn(populationSize)
		sample = append(sample, population[j])
		population = append(population[:j], population[j+1:]...)
		populationSize--
	}
	return sample
}

func assertConnectionSucceeds(sourceApp string, destApps []string, ports []int, nProxies int) {
	parallelRunner := &testsupport.ParallelRunner{
		NumWorkers: 50 * nProxies,
	}
	parallelRunner.RunOnSliceStrings(destApps, func(appIP string) {
		for _, port := range ports {
			assertSingleConnection(appIP, port, sourceApp, true)
		}
	})
}

func assertConnectionFails(sourceApp string, destApps []string, ports []int, nProxies int) {
	parallelRunner := &testsupport.ParallelRunner{
		NumWorkers: 50 * nProxies,
	}
	parallelRunner.RunOnSliceStrings(destApps, func(appIP string) {
		for _, port := range ports {
			assertSingleConnection(appIP, port, sourceApp, false)
		}
	})
}

func assertSingleConnection(destIP string, port int, sourceAppName string, shouldSucceed bool) {
	if shouldSucceed {
		assertResponseContains(destIP, port, sourceAppName, "application_name")
	} else {
		assertResponseContains(destIP, port, sourceAppName, "request failed")
	}
}

func assertResponseContains(destIP string, port int, sourceAppName string, desiredResponse string) {
	proxyTest := func() (string, error) {
		resp, err := httpGetBytes(fmt.Sprintf("http://%s.%s/proxy/%s:%d", sourceAppName, config.AppsDomain, destIP, port))
		if err != nil {
			return "", err
		}
		return string(resp.Body), nil
	}
	Eventually(proxyTest, 10*time.Second, 500*time.Millisecond).Should(ContainSubstring(desiredResponse))
}

var httpClient = &http.Client{
	Transport: &http.Transport{
		DisableKeepAlives: true,
		Dial: (&net.Dialer{
			Timeout:   4 * time.Second,
			KeepAlive: 0,
		}).Dial,
	},
}

type httpResp struct {
	StatusCode int
	Body       []byte
}

func httpGetBytes(url string) (httpResp, error) {
	resp, err := httpClient.Get(url)
	if err != nil {
		return httpResp{}, err
	}
	defer resp.Body.Close()

	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return httpResp{}, err
	}

	return httpResp{resp.StatusCode, respBytes}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/kataras/iris"
	"github.com/kataras/iris/sessions"

	"github.com/kataras/iris/mvc"
)

// TODO: It's not here but this file is what I'll see before the commit in order to delete it:
// Think a way to simplify the router cycle, I did create it to support any type of router
// but as I see nobody wants to override the iris router's behavior(I'm not speaking about wrapper, this will stay of course because it's useful on security-critical middlewares) because it's the best by far.
// Therefore I should reduce some "freedom of change" for the shake of code maintanability in the core/router files: handler.go | router.go and single change on APIBuilder's field.
func main() {
	app := iris.New()
	app.Logger().SetLevel("debug")
	mvc.New(app.Party("/todo")).Configure(TodoApp)
	// no let's have a clear "mvc" package without any conversions and type aliases,
	// it's one extra import path for a whole new world, it worths it.
	//
	// app.UseMVC(app.Party("/todo")).Configure(func(app *iris.MVCApplication))

	app.Run(iris.Addr(":8080"))
}

func TodoApp(app *mvc.Application) {
	// You can use normal middlewares at MVC apps of course.
	app.Router.Use(func(ctx iris.Context) {
		ctx.Application().Logger().Infof("Path: %s", ctx.Path())
		ctx.Next()
	})

	// Add dependencies which will be binding to the controller(s),
	// can be either a function which accepts an iris.Context and returns a single value (dynamic binding)
	// or a static struct value (service).
	app.AddDependencies(
		sessions.New(sessions.Config{})).Start,
		&prefixedLogger{prefix: "DEV"},
	)

	app.Register(new(TodoController))

	// All dependencies of the parent *mvc.Application
	// are cloned to that new child, thefore it has access to the same session as well.
	app.NewChild(app.Router.Party("/sub")).
		Register(new(TodoSubController))
}

// If controller's fields (or even its functions) expecting an interface
// but a struct value is binded then it will check if that struct value implements
// the interface and if true then it will bind it as expected.

type LoggerService interface {
	Log(string)
}

type prefixedLogger struct {
	prefix string
}

func (s *prefixedLogger) Log(msg string) {
	fmt.Printf("%s: %s\n", s.prefix, msg)
}

type TodoController struct {
	Logger LoggerService

	Session *sessions.Session
}

func (c *TodoController) BeforeActivation(b mvc.BeforeActivation) {
	b.Handle("GET", "/custom", "Custom")
}

func (c *TodoController) AfterActivation(a mvc.AfterActivation) {
	if a.Singleton() {
		panic("TodoController should be stateless, a request-scoped, we have a 'Session' which depends on the context.")
	}
}

func (c *TodoController) Get() string {
	count := c.Session.Increment("count", 1)

	body := fmt.Sprintf("Hello from TodoController\nTotal visits from you: %d", count)
	c.Logger.Log(body)
	return body
}

func (c *TodoController) Custom() string {
	return "custom"
}

type TodoSubController struct {
	Session *sessions.Session
}

func (c *TodoSubController) Get() string {
	count, _ := c.Session.GetIntDefault("count", 1)
	return fmt.Sprintf("Hello from TodoSubController.\nRead-only visits count: %d", count)
}
<commit_msg>fix trailing parenthesis on the mvc/ideas/1/main.go which will be removed at the next commits, this was my dream and I made it reality in just 50 hours :P<commit_after>package main

import (
	"fmt"

	"github.com/kataras/iris"
	"github.com/kataras/iris/sessions"

	"github.com/kataras/iris/mvc"
)

// TODO: It's not here but this file is what I'll see before the commit in order to delete it:
// Think a way to simplify the router cycle, I did create it to support any type of router
// but as I see nobody wants to override the iris router's behavior(I'm not speaking about wrapper, this will stay of course because it's useful on security-critical middlewares) because it's the best by far.
// Therefore I should reduce some "freedom of change" for the shake of code maintanability in the core/router files: handler.go | router.go and single change on APIBuilder's field.
func main() {
	app := iris.New()
	app.Logger().SetLevel("debug")
	mvc.New(app.Party("/todo")).Configure(TodoApp)
	// no let's have a clear "mvc" package without any conversions and type aliases,
	// it's one extra import path for a whole new world, it worths it.
	//
	// app.UseMVC(app.Party("/todo")).Configure(func(app *iris.MVCApplication))

	app.Run(iris.Addr(":8080"))
}

func TodoApp(app *mvc.Application) {
	// You can use normal middlewares at MVC apps of course.
	app.Router.Use(func(ctx iris.Context) {
		ctx.Application().Logger().Infof("Path: %s", ctx.Path())
		ctx.Next()
	})

	// Add dependencies which will be binding to the controller(s),
	// can be either a function which accepts an iris.Context and returns a single value (dynamic binding)
	// or a static struct value (service).
	app.AddDependencies(
		sessions.New(sessions.Config{}).Start,
		&prefixedLogger{prefix: "DEV"},
	)

	app.Register(new(TodoController))

	// All dependencies of the parent *mvc.Application
	// are cloned to that new child, thefore it has access to the same session as well.
	app.NewChild(app.Router.Party("/sub")).
		Register(new(TodoSubController))
}

// If controller's fields (or even its functions) expecting an interface
// but a struct value is binded then it will check if that struct value implements
// the interface and if true then it will bind it as expected.

type LoggerService interface {
	Log(string)
}

type prefixedLogger struct {
	prefix string
}

func (s *prefixedLogger) Log(msg string) {
	fmt.Printf("%s: %s\n", s.prefix, msg)
}

type TodoController struct {
	Logger LoggerService

	Session *sessions.Session
}

func (c *TodoController) BeforeActivation(b mvc.BeforeActivation) {
	b.Handle("GET", "/custom", "Custom")
}

func (c *TodoController) AfterActivation(a mvc.AfterActivation) {
	if a.Singleton() {
		panic("TodoController should be stateless, a request-scoped, we have a 'Session' which depends on the context.")
	}
}

func (c *TodoController) Get() string {
	count := c.Session.Increment("count", 1)

	body := fmt.Sprintf("Hello from TodoController\nTotal visits from you: %d", count)
	c.Logger.Log(body)
	return body
}

func (c *TodoController) Custom() string {
	return "custom"
}

type TodoSubController struct {
	Session *sessions.Session
}

func (c *TodoSubController) Get() string {
	count, _ := c.Session.GetIntDefault("count", 1)
	return fmt.Sprintf("Hello from TodoSubController.\nRead-only visits count: %d", count)
}
<|endoftext|>


<commit_before>//go:generate stringer -type=event
//go:generate gopherjs build

// +build js
package main

import (
	"fmt"
	"log"
	"sync"

	"github.com/ghthor/aodd/game"
	"github.com/ghthor/aodd/game/client"
	"github.com/ghthor/aodd/game/client/canvas"
	"github.com/ghthor/engine/rpg2d"
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/entity"
	"github.com/ghthor/engine/sim/stime"

	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/websocket"
)

type jsObject map[string]interface{}
type jsArray []interface{}

type EventPublisher interface {
	Emit(fmt.Stringer, jsArray)
}

type eventPublisher struct {
	*js.Object
}

func (e eventPublisher) Emit(event fmt.Stringer, params jsArray) {
	e.Call("emit", event.String(), params)
}

type event int

const (
	EV_ERROR event = iota
	EV_CONNECTED

	EV_ACTOR_DOESNT_EXIST
	EV_ACTOR_EXISTS
	EV_AUTH_FAILED

	EV_LOGIN_SUCCESS
	EV_CREATE_SUCCESS

	// Come together in the same response from the server
	EV_RECV_INPUT_CONN
	EV_RECV_INITIAL_STATE

	EV_RECV_UPDATE

	EV_RECV_CHAT_SAY
	EV_SENT_CHAT_SAY

	EV_TERRAIN_RESET
	EV_TERRAIN_CANVAS_SHIFT
	EV_TERRAIN_DRAW_TILE

	EV_SIZE
)

// Key used on the window object
// window.gopherjsApplication
const moduleKey = "gopherjsApplication"

const undefined = "undefined"

func main() {
	js.Global.Set(moduleKey, jsObject{
		"moduleKey":  moduleKey,
		"initialize": initialize,
	})
}

// This function will be called once by requirejs
// from the shim configuration. The object that is
// returned here is what will be available as the
// require("app") module.
func initialize(settings *js.Object) jsObject {
	module := jsObject{
		"moduleKey": moduleKey,

		// Provide a function to dial the server.
		// pub should be an object that has been
		// extended with minpubsub.
		"dial": func(pub *js.Object) {
			if pub.Get("emit").String() == undefined {
				log.Println("invalid publisher: missing emit() function")
				return
			}

			go func() {
				wsUrl := settings.Get("websocketURL").String()

				ws, err := websocket.Dial(wsUrl)
				if err != nil {
					log.Fatal(err)
				}

				loginConn := client.NewLoginConn(game.NewGobConn(ws))
				pub := eventPublisher{pub}

				// Emit a connected event and a object the
				// login form can use to send messages to the
				// server.
				pub.Emit(EV_CONNECTED, jsArray{newLoginConn(loginConn, pub)})
			}()
		},
	}

	for i := EV_ERROR; i < EV_SIZE; i++ {
		module[event(i).String()] = event(i).String()
	}

	coordModule := make(jsObject, int(coord.West)+1)

	for i := 0; i <= int(coord.West); i++ {
		coordModule[coord.Direction(i).String()] = coord.Direction(i)
	}

	gameModule := make(jsObject, int(game.MR_SIZE)+int(game.UR_SIZE)+int(game.CR_SIZE))

	for i := game.MR_ERROR; i < game.MR_SIZE; i++ {
		gameModule[game.MoveRequestType(i).String()] = game.MoveRequestType(i)
	}

	for i := game.UR_ERROR; i < game.UR_SIZE; i++ {
		gameModule[game.UseRequestType(i).String()] = game.UseRequestType(i)
	}

	for i := game.CR_ERROR; i < game.CR_SIZE; i++ {
		gameModule[game.ChatRequestType(i).String()] = game.ChatRequestType(i)
	}

	// require("github.com/ghthor/engine/rpg2d/coord")
	module["coord"] = coordModule
	// require("github.com/ghthor/aodd/game")
	module["game"] = gameModule

	return module
}

func newLoginConn(loginConn client.LoginConn, pub eventPublisher) jsObject {
	var conn sync.Mutex

	return jsObject{
		"attemptLogin": func(name, password string) {
			go func() {
				conn.Lock()
				defer conn.Unlock()

				trip := loginConn.AttemptLogin(name, password)

				select {
				case actorDoesntExist := <-trip.ActorDoesntExist:
					pub.Emit(EV_ACTOR_DOESNT_EXIST, jsArray{
						actorDoesntExist.Name,
						actorDoesntExist.Password,
					})

				case authFailed := <-trip.AuthFailed:
					js.Debugger()
					pub.Emit(EV_AUTH_FAILED, jsArray{authFailed.Name})

				case resp := <-trip.Success:
					pub.Emit(EV_LOGIN_SUCCESS, jsArray{resp.Name, newLoggedInConn(resp.Name, resp.LoggedInConn)})

				case err := <-trip.Error:
					pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
				}
			}()
		},

		"createActor": func(name, password string) {
			go func() {
				conn.Lock()
				defer conn.Unlock()

				trip := loginConn.CreateActor(name, password)

				select {
				case actorExists := <-trip.ActorExists:
					pub.Emit(EV_ACTOR_EXISTS, jsArray{actorExists.Name})

				case resp := <-trip.Success:
					pub.Emit(EV_LOGIN_SUCCESS, jsArray{resp.Name, newLoggedInConn(resp.Name, resp.LoggedInConn)})

				case err := <-trip.Error:
					pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
				}
			}()
		},
	}
}

type world struct {
	mu sync.RWMutex

	entity game.ActorEntityState
	state  rpg2d.WorldState
}

func (w *world) now() stime.Time {
	w.mu.RLock()
	defer w.mu.RUnlock()

	return w.state.Time
}

func (w *world) update(diff rpg2d.WorldStateDiff) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Update the state
	w.state.Apply(diff)

	// Update the entity
	for _, e := range w.state.Entities {
		if e.EntityId() == w.entity.EntityId() {
			w.entity = e.(game.ActorEntityState)
			break
		}
	}
}

func (w *world) actorEntityById(id entity.Id) (game.ActorEntityState, error) {
	w.mu.RLock()
	defer w.mu.RUnlock()
	var err error

	for _, e := range w.state.Entities {
		if e.EntityId() == id {
			switch e := e.(type) {
			case game.ActorEntityState:
				return e, nil

			default:
				err = fmt.Errorf("expected e.(game.ActorEntityState), got e.(%T)", e)
			}

			break
		}
	}

	return game.ActorEntityState{}, err
}

type terrainCanvas struct {
	pub EventPublisher
}

func (c terrainCanvas) Reset(slice rpg2d.TerrainMapStateSlice) {
	c.pub.Emit(EV_TERRAIN_RESET, jsArray{slice})
}

func (c terrainCanvas) Shift(dir canvas.TerrainShift, mags canvas.TerrainShiftMagnitudes) {
	for dir, mag := range mags {
		c.pub.Emit(EV_TERRAIN_CANVAS_SHIFT, jsArray{dir, mag})
	}
}

func (c terrainCanvas) DrawTile(ttype rpg2d.TerrainType, cell coord.Cell) {
	c.pub.Emit(EV_TERRAIN_DRAW_TILE, jsArray{ttype, cell})
}

func newLoggedInConn(name string, loggedInConn client.LoggedInConn) jsObject {
	return jsObject{
		"connectActor": func(pub *js.Object) {
			if pub.Get("emit").String() == undefined {
				log.Println("invalid publisher: missing emit() function")
				return
			}

			go func() {
				pub := eventPublisher{pub}

				trip := loggedInConn.ConnectActor(name)

				select {
				case resp := <-trip.Connected:
					world := world{
						entity: resp.InitialState.Entity,
						state:  resp.InitialState.WorldState,
					}

					pub.Emit(EV_RECV_INPUT_CONN, jsArray{newInputConn(&world, resp.InputConn, pub)})
					pub.Emit(EV_RECV_INITIAL_STATE, jsArray{
						resp.InitialState.Entity,
						resp.InitialState.WorldState,
						jsObject{
							"Bounds":  resp.InitialState.WorldState.Bounds,
							"Terrain": resp.InitialState.WorldState.TerrainMap.String(),
						},
					})

					for {
						update, err := resp.NextUpdate()
						if err != nil {
							pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
							return
						}

						// TODO Fix unsafe concurrent access of world.state
						err = canvas.ApplyTerrainDiff(terrainCanvas{pub}, world.state, update)
						if err != nil {
							pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
						}

						world.update(update)

						for _, e := range update.Entities {
							switch e := e.(type) {
							case game.SayEntityState:
								err := emitChatRecvEvent(&world, e, pub)
								if err != nil {
									pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
								}
							}
						}

						pub.Emit(EV_RECV_UPDATE, jsArray{update})
					}

				case err := <-trip.Error:
					pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
				}
			}()
		},
	}
}

func emitChatRecvEvent(world *world, say game.SayEntityState, pub EventPublisher) error {
	actor, err := world.actorEntityById(say.SaidBy)
	if err != nil {
		return err
	}

	pub.Emit(EV_RECV_CHAT_SAY, jsArray{say.Id, actor.Name, say.Msg, say.SaidAt})
	return nil
}

func newInputConn(world *world, conn client.InputConn, pub EventPublisher) jsObject {
	return jsObject{
		"sendMoveRequest": func(typ game.MoveRequestType, d coord.Direction) {
			go func() {
				conn.SendMoveRequest(game.MoveRequest{
					MoveRequestType: typ,
					Time:            world.now(),
					Direction:       d,
				})
			}()
		},

		"sendUseRequest": func(typ game.UseRequestType, skill string) {
			go func() {
				conn.SendUseRequest(game.UseRequest{
					UseRequestType: typ,
					Time:           world.now(),
					Skill:          skill,
				})
			}()
		},

		"sendChatRequest": func(typ game.ChatRequestType, msg string) {
			go func() {
				conn.SendChatRequest(game.ChatRequest{
					ChatRequestType: typ,
					Time:            world.now(),
					Msg:             msg,
				})
			}()

			pub.Emit(EV_SENT_CHAT_SAY, nil)
		},
	}
}
<commit_msg>[www/js/app] emit correct event on actor create success<commit_after>//go:generate stringer -type=event
//go:generate gopherjs build

// +build js
package main

import (
	"fmt"
	"log"
	"sync"

	"github.com/ghthor/aodd/game"
	"github.com/ghthor/aodd/game/client"
	"github.com/ghthor/aodd/game/client/canvas"
	"github.com/ghthor/engine/rpg2d"
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/entity"
	"github.com/ghthor/engine/sim/stime"

	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/websocket"
)

type jsObject map[string]interface{}
type jsArray []interface{}

type EventPublisher interface {
	Emit(fmt.Stringer, jsArray)
}

type eventPublisher struct {
	*js.Object
}

func (e eventPublisher) Emit(event fmt.Stringer, params jsArray) {
	e.Call("emit", event.String(), params)
}

type event int

const (
	EV_ERROR event = iota
	EV_CONNECTED

	EV_ACTOR_DOESNT_EXIST
	EV_ACTOR_EXISTS
	EV_AUTH_FAILED

	EV_LOGIN_SUCCESS
	EV_CREATE_SUCCESS

	// Come together in the same response from the server
	EV_RECV_INPUT_CONN
	EV_RECV_INITIAL_STATE

	EV_RECV_UPDATE

	EV_RECV_CHAT_SAY
	EV_SENT_CHAT_SAY

	EV_TERRAIN_RESET
	EV_TERRAIN_CANVAS_SHIFT
	EV_TERRAIN_DRAW_TILE

	EV_SIZE
)

// Key used on the window object
// window.gopherjsApplication
const moduleKey = "gopherjsApplication"

const undefined = "undefined"

func main() {
	js.Global.Set(moduleKey, jsObject{
		"moduleKey":  moduleKey,
		"initialize": initialize,
	})
}

// This function will be called once by requirejs
// from the shim configuration. The object that is
// returned here is what will be available as the
// require("app") module.
func initialize(settings *js.Object) jsObject {
	module := jsObject{
		"moduleKey": moduleKey,

		// Provide a function to dial the server.
		// pub should be an object that has been
		// extended with minpubsub.
		"dial": func(pub *js.Object) {
			if pub.Get("emit").String() == undefined {
				log.Println("invalid publisher: missing emit() function")
				return
			}

			go func() {
				wsUrl := settings.Get("websocketURL").String()

				ws, err := websocket.Dial(wsUrl)
				if err != nil {
					log.Fatal(err)
				}

				loginConn := client.NewLoginConn(game.NewGobConn(ws))
				pub := eventPublisher{pub}

				// Emit a connected event and a object the
				// login form can use to send messages to the
				// server.
				pub.Emit(EV_CONNECTED, jsArray{newLoginConn(loginConn, pub)})
			}()
		},
	}

	for i := EV_ERROR; i < EV_SIZE; i++ {
		module[event(i).String()] = event(i).String()
	}

	coordModule := make(jsObject, int(coord.West)+1)

	for i := 0; i <= int(coord.West); i++ {
		coordModule[coord.Direction(i).String()] = coord.Direction(i)
	}

	gameModule := make(jsObject, int(game.MR_SIZE)+int(game.UR_SIZE)+int(game.CR_SIZE))

	for i := game.MR_ERROR; i < game.MR_SIZE; i++ {
		gameModule[game.MoveRequestType(i).String()] = game.MoveRequestType(i)
	}

	for i := game.UR_ERROR; i < game.UR_SIZE; i++ {
		gameModule[game.UseRequestType(i).String()] = game.UseRequestType(i)
	}

	for i := game.CR_ERROR; i < game.CR_SIZE; i++ {
		gameModule[game.ChatRequestType(i).String()] = game.ChatRequestType(i)
	}

	// require("github.com/ghthor/engine/rpg2d/coord")
	module["coord"] = coordModule
	// require("github.com/ghthor/aodd/game")
	module["game"] = gameModule

	return module
}

func newLoginConn(loginConn client.LoginConn, pub eventPublisher) jsObject {
	var conn sync.Mutex

	return jsObject{
		"attemptLogin": func(name, password string) {
			go func() {
				conn.Lock()
				defer conn.Unlock()

				trip := loginConn.AttemptLogin(name, password)

				select {
				case actorDoesntExist := <-trip.ActorDoesntExist:
					pub.Emit(EV_ACTOR_DOESNT_EXIST, jsArray{
						actorDoesntExist.Name,
						actorDoesntExist.Password,
					})

				case authFailed := <-trip.AuthFailed:
					js.Debugger()
					pub.Emit(EV_AUTH_FAILED, jsArray{authFailed.Name})

				case resp := <-trip.Success:
					pub.Emit(EV_LOGIN_SUCCESS, jsArray{resp.Name, newLoggedInConn(resp.Name, resp.LoggedInConn)})

				case err := <-trip.Error:
					pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
				}
			}()
		},

		"createActor": func(name, password string) {
			go func() {
				conn.Lock()
				defer conn.Unlock()

				trip := loginConn.CreateActor(name, password)

				select {
				case actorExists := <-trip.ActorExists:
					pub.Emit(EV_ACTOR_EXISTS, jsArray{actorExists.Name})

				case resp := <-trip.Success:
					pub.Emit(EV_CREATE_SUCCESS, jsArray{resp.Name, newLoggedInConn(resp.Name, resp.LoggedInConn)})

				case err := <-trip.Error:
					pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
				}
			}()
		},
	}
}

type world struct {
	mu sync.RWMutex

	entity game.ActorEntityState
	state  rpg2d.WorldState
}

func (w *world) now() stime.Time {
	w.mu.RLock()
	defer w.mu.RUnlock()

	return w.state.Time
}

func (w *world) update(diff rpg2d.WorldStateDiff) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Update the state
	w.state.Apply(diff)

	// Update the entity
	for _, e := range w.state.Entities {
		if e.EntityId() == w.entity.EntityId() {
			w.entity = e.(game.ActorEntityState)
			break
		}
	}
}

func (w *world) actorEntityById(id entity.Id) (game.ActorEntityState, error) {
	w.mu.RLock()
	defer w.mu.RUnlock()
	var err error

	for _, e := range w.state.Entities {
		if e.EntityId() == id {
			switch e := e.(type) {
			case game.ActorEntityState:
				return e, nil

			default:
				err = fmt.Errorf("expected e.(game.ActorEntityState), got e.(%T)", e)
			}

			break
		}
	}

	return game.ActorEntityState{}, err
}

type terrainCanvas struct {
	pub EventPublisher
}

func (c terrainCanvas) Reset(slice rpg2d.TerrainMapStateSlice) {
	c.pub.Emit(EV_TERRAIN_RESET, jsArray{slice})
}

func (c terrainCanvas) Shift(dir canvas.TerrainShift, mags canvas.TerrainShiftMagnitudes) {
	for dir, mag := range mags {
		c.pub.Emit(EV_TERRAIN_CANVAS_SHIFT, jsArray{dir, mag})
	}
}

func (c terrainCanvas) DrawTile(ttype rpg2d.TerrainType, cell coord.Cell) {
	c.pub.Emit(EV_TERRAIN_DRAW_TILE, jsArray{ttype, cell})
}

func newLoggedInConn(name string, loggedInConn client.LoggedInConn) jsObject {
	return jsObject{
		"connectActor": func(pub *js.Object) {
			if pub.Get("emit").String() == undefined {
				log.Println("invalid publisher: missing emit() function")
				return
			}

			go func() {
				pub := eventPublisher{pub}

				trip := loggedInConn.ConnectActor(name)

				select {
				case resp := <-trip.Connected:
					world := world{
						entity: resp.InitialState.Entity,
						state:  resp.InitialState.WorldState,
					}

					pub.Emit(EV_RECV_INPUT_CONN, jsArray{newInputConn(&world, resp.InputConn, pub)})
					pub.Emit(EV_RECV_INITIAL_STATE, jsArray{
						resp.InitialState.Entity,
						resp.InitialState.WorldState,
						jsObject{
							"Bounds":  resp.InitialState.WorldState.Bounds,
							"Terrain": resp.InitialState.WorldState.TerrainMap.String(),
						},
					})

					for {
						update, err := resp.NextUpdate()
						if err != nil {
							pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
							return
						}

						// TODO Fix unsafe concurrent access of world.state
						err = canvas.ApplyTerrainDiff(terrainCanvas{pub}, world.state, update)
						if err != nil {
							pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
						}

						world.update(update)

						for _, e := range update.Entities {
							switch e := e.(type) {
							case game.SayEntityState:
								err := emitChatRecvEvent(&world, e, pub)
								if err != nil {
									pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
								}
							}
						}

						pub.Emit(EV_RECV_UPDATE, jsArray{update})
					}

				case err := <-trip.Error:
					pub.Emit(EV_ERROR, jsArray{jsObject{"error": err.Error()}})
				}
			}()
		},
	}
}

func emitChatRecvEvent(world *world, say game.SayEntityState, pub EventPublisher) error {
	actor, err := world.actorEntityById(say.SaidBy)
	if err != nil {
		return err
	}

	pub.Emit(EV_RECV_CHAT_SAY, jsArray{say.Id, actor.Name, say.Msg, say.SaidAt})
	return nil
}

func newInputConn(world *world, conn client.InputConn, pub EventPublisher) jsObject {
	return jsObject{
		"sendMoveRequest": func(typ game.MoveRequestType, d coord.Direction) {
			go func() {
				conn.SendMoveRequest(game.MoveRequest{
					MoveRequestType: typ,
					Time:            world.now(),
					Direction:       d,
				})
			}()
		},

		"sendUseRequest": func(typ game.UseRequestType, skill string) {
			go func() {
				conn.SendUseRequest(game.UseRequest{
					UseRequestType: typ,
					Time:           world.now(),
					Skill:          skill,
				})
			}()
		},

		"sendChatRequest": func(typ game.ChatRequestType, msg string) {
			go func() {
				conn.SendChatRequest(game.ChatRequest{
					ChatRequestType: typ,
					Time:            world.now(),
					Msg:             msg,
				})
			}()

			pub.Emit(EV_SENT_CHAT_SAY, nil)
		},
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
)

type logItem struct {
	dt  time.Time
	msg string
}

// AuditLog - Audit log helper
type AuditLog struct {
	log       *logrus.Logger
	logSource string
	dbUtils   *DbUtils
	queue     chan logItem
	wg        *sync.WaitGroup
}

// SetWaitGroup - SetWaitGroup
func (a *AuditLog) SetWaitGroup(wg *sync.WaitGroup) {
	a.wg = wg
}

// SetLogger - SetLogger
func (a *AuditLog) SetLogger(logSource string, log *logrus.Logger, dbUtils *DbUtils) {
	a.log = log
	a.logSource = logSource
	a.dbUtils = dbUtils
	a.queue = make(chan logItem, 128)
}

func (a *AuditLog) processQueue() {
	if a.wg != nil {
		defer a.wg.Done()
	}

	li := <-a.queue

	pq := a.dbUtils.PQuery(`
		INSERT INTO audit_log (
			log_time, log_source, audit_msg
		)
		VALUES (?, ?, ?)
	`, li.dt,
		a.logSource,
		li.msg)

	_, err := a.dbUtils.Exec(pq)
	if err != nil {
		fmt.Println("log error: ", err)
	}
}

func (a AuditLog) Write(p []byte) (n int, err error) {
	if a.wg != nil {
		a.wg.Add(1)
	}

	li := logItem{
		dt:  time.Now().UTC(),
		msg: string(p),
	}

	a.queue <- li

	go a.processQueue()

	return len(p), nil
}

// Log - Log Helper function
func (a AuditLog) Log(err error, msgType string, msg string, details ...interface{}) {
	fields := make(map[string]interface{})

	if len(msgType) > 0 {
		fields["msg_type"] = msgType
	}

	isErr := false
	if err != nil {
		isErr = true
	}

	if isErr {
		fields["status"] = "failed"
	} else {
		fields["status"] = "successful"
	}

	if details != nil {
		var key string

		for i, detail := range details {
			if i%2 == 0 {
				key = detail.(string)
			} else {
				if detail != nil {
					if reflect.ValueOf(detail).Kind() == reflect.Ptr {
						fields[key] = reflect.Indirect(reflect.ValueOf(detail)).Elem()
					} else {
						fields[key] = detail
					}
				}
			}
		}
	}

	hasKeys := false

	if len(fields) > 0 {
		hasKeys = true
	}

	if isErr {
		if hasKeys {
			if err != nil {
				a.log.WithError(err).WithFields(fields).Error(msg)
			} else {
				a.log.WithFields(fields).Error(msg)
			}
		} else {
			if err != nil {
				a.log.WithError(err).Error(msg)
			} else {
				a.log.Error(msg)
			}
		}
	} else {
		if hasKeys {
			if err != nil {
				a.log.WithError(err).WithFields(fields).Info(msg)
			} else {
				a.log.WithFields(fields).Info(msg)
			}
		} else {
			if err != nil {
				a.log.WithError(err).Info(msg)
			} else {
				a.log.Info(msg)
			}
		}
	}
}
<commit_msg>BugFix - couldnt log structs<commit_after>package utils

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
)

type logItem struct {
	dt  time.Time
	msg string
}

// AuditLog - Audit log helper
type AuditLog struct {
	log       *logrus.Logger
	logSource string
	dbUtils   *DbUtils
	queue     chan logItem
	wg        *sync.WaitGroup
}

// SetWaitGroup - SetWaitGroup
func (a *AuditLog) SetWaitGroup(wg *sync.WaitGroup) {
	a.wg = wg
}

// SetLogger - SetLogger
func (a *AuditLog) SetLogger(logSource string, log *logrus.Logger, dbUtils *DbUtils) {
	a.log = log
	a.logSource = logSource
	a.dbUtils = dbUtils
	a.queue = make(chan logItem, 128)
}

func (a *AuditLog) processQueue() {
	if a.wg != nil {
		defer a.wg.Done()
	}

	li := <-a.queue

	pq := a.dbUtils.PQuery(`
		INSERT INTO audit_log (
			log_time, log_source, audit_msg
		)
		VALUES (?, ?, ?)
	`, li.dt,
		a.logSource,
		li.msg)

	_, err := a.dbUtils.Exec(pq)
	if err != nil {
		fmt.Println("log error: ", err)
	}
}

func (a AuditLog) Write(p []byte) (n int, err error) {
	if a.wg != nil {
		a.wg.Add(1)
	}

	li := logItem{
		dt:  time.Now().UTC(),
		msg: string(p),
	}

	a.queue <- li

	go a.processQueue()

	return len(p), nil
}

// Log - Log Helper function
func (a AuditLog) Log(err error, msgType string, msg string, details ...interface{}) {
	fields := make(map[string]interface{})

	if len(msgType) > 0 {
		fields["msg_type"] = msgType
	}

	isErr := false
	if err != nil {
		isErr = true
	}

	if isErr {
		fields["status"] = "failed"
	} else {
		fields["status"] = "successful"
	}

	if details != nil {
		var key string

		for i, detail := range details {
			if i%2 == 0 {
				key = detail.(string)
			} else {
				if detail != nil {
					if reflect.ValueOf(detail).Kind() == reflect.Ptr {
						fields[key] = reflect.Indirect(reflect.ValueOf(detail)).Interface()
					} else {
						fields[key] = detail
					}
				}
			}
		}
	}

	hasKeys := false

	if len(fields) > 0 {
		hasKeys = true
	}

	if isErr {
		if hasKeys {
			if err != nil {
				a.log.WithError(err).WithFields(fields).Error(msg)
			} else {
				a.log.WithFields(fields).Error(msg)
			}
		} else {
			if err != nil {
				a.log.WithError(err).Error(msg)
			} else {
				a.log.Error(msg)
			}
		}
	} else {
		if hasKeys {
			if err != nil {
				a.log.WithError(err).WithFields(fields).Info(msg)
			} else {
				a.log.WithFields(fields).Info(msg)
			}
		} else {
			if err != nil {
				a.log.WithError(err).Info(msg)
			} else {
				a.log.Info(msg)
			}
		}
	}
}
<|endoftext|>


<commit_before>package models

import (
	"github.com/nsqio/go-nsq"
	"time"
)

// RestoreState stores information about the state of a bag restoration
// operation. This entire structure will be converted to JSON and saved
// as a WorkItemState object in Pharos.
type RestoreState struct {
	// NSQMessage is the NSQ message being processed in this restore
	// request. Not serialized because it will change each time we
	// try to process a request.
	NSQMessage *nsq.Message `json"-"`
	// WorkItem is the Pharos WorkItem we're processing.
	// Not serialized because the Pharos WorkItem record will be
	// more up-to-date and authoritative.
	WorkItem *WorkItem `json"-"`
	// IntellectualObject is the object we're restoring. Not serialized
	// because if the object has thousands of files, the serialization is
	// huge.
	IntellectualObject *IntellectualObject `json"-"`
	// PackageSummary contains information about the outcome of the
	// attempt to reassemble this bag for restoration.
	PackageSummary *WorkSummary
	// ValidateSummary contains information about the outcome
	// of validating this newly reassembled bag. We must validate
	// it before sending it to the restoration bucket.
	ValidateSummary *WorkSummary
	// CopySummary contains information about the outcome of the
	// attempt to copy the tarred bag to the depositor's restoration
	// bucket.
	CopySummary *WorkSummary
	// RecordSummary contains information about the outcome of
	// attempts to record the restoration event and the completion
	// of the WorkItem in Pharos.
	RecordSummary *WorkSummary
	// LocalBagDir is the absolute path to the untarred bag. We'll be
	// assembling the bag contents in this directory.
	LocalBagDir string
	// LocalTarFile is the absolute path the tarred version of this
	// bag. The local tar file will not exist until the bag has been
	// fully assembled and tarred.
	LocalTarFile string
	// RestoredToUrl is a URL that points to the copy of this bag
	// in the depositor's S3 restoration bucket.
	RestoredToUrl string
	// CopiedToRestorationAt is a timestamp describing when the
	// reassembled bag was copied to the depositor's S3 restoration
	// bucket.
	CopiedToRestorationAt time.Time
}

// NewRestoreState creates a new RestoreState object with empty
// PackageSummary, RestoreSummary, and ValidationSummary.
func NewRestoreState(message *nsq.Message) *RestoreState {
	return &RestoreState{
		NSQMessage:      message,
		PackageSummary:  NewWorkSummary(),
		ValidateSummary: NewWorkSummary(),
		RecordSummary:   NewWorkSummary(),
		CopySummary:     NewWorkSummary(),
	}
}

// HasErrors returns true if any of the work summaries have errors.
func (restoreState *RestoreState) HasErrors() bool {
	return restoreState.PackageSummary.HasErrors() ||
		restoreState.ValidateSummary.HasErrors() ||
		restoreState.RecordSummary.HasErrors() ||
		restoreState.CopySummary.HasErrors()
}

// HasFatalErrors returns true if any of the work summaries have
// a fatal error.
func (restoreState *RestoreState) HasFatalErrors() bool {
	return (restoreState.PackageSummary.ErrorIsFatal ||
		restoreState.ValidateSummary.ErrorIsFatal ||
		restoreState.RecordSummary.ErrorIsFatal ||
		restoreState.CopySummary.ErrorIsFatal)
}

// AllErrorsAsString returns all error messages from all work summaries
// as a single string.
func (restoreState *RestoreState) AllErrorsAsString() string {
	errors := []string{
		restoreState.PackageSummary.AllErrorsAsString(),
		restoreState.ValidateSummary.AllErrorsAsString(),
		restoreState.RecordSummary.AllErrorsAsString(),
		restoreState.CopySummary.AllErrorsAsString(),
	}
	allErrors := ""
	for _, err := range errors {
		if err != "" {
			allErrors += err + "\n"
		}
	}
	return allErrors
}

// MostRecentSummary returns the WorkSummary for the most recent
// operation.
func (restoreState *RestoreState) MostRecentSummary() *WorkSummary {
	summary := restoreState.PackageSummary
	if restoreState.RecordSummary.Started() {
		summary = restoreState.RecordSummary
	} else if restoreState.CopySummary.Started() {
		summary = restoreState.CopySummary
	} else if restoreState.ValidateSummary.Started() {
		summary = restoreState.ValidateSummary
	}
	return summary
}

// TouchNSQ tells NSQ we're still working on this item.
func (restoreState *RestoreState) TouchNSQ() {
	if restoreState.NSQMessage != nil {
		restoreState.NSQMessage.Touch()
	}
}

// FinishNSQ tells NSQ we're done with this message.
func (restoreState *RestoreState) FinishNSQ() {
	if restoreState.NSQMessage != nil {
		restoreState.NSQMessage.Finish()
	}
}

// RequeueNSQ tells NSQ to give this item to give this item to another
// worker (or perhaps the same worker) after a delay of at least the
// specified number of milliseconds.
func (restoreState *RestoreState) RequeueNSQ(milliseconds int) {
	if restoreState.NSQMessage != nil {
		restoreState.NSQMessage.Requeue(time.Duration(milliseconds) * time.Millisecond)
	}
}
<commit_msg>Fixed json serialization<commit_after>package models

import (
	"github.com/nsqio/go-nsq"
	"time"
)

// RestoreState stores information about the state of a bag restoration
// operation. This entire structure will be converted to JSON and saved
// as a WorkItemState object in Pharos.
type RestoreState struct {
	// NSQMessage is the NSQ message being processed in this restore
	// request. Not serialized because it will change each time we
	// try to process a request.
	NSQMessage *nsq.Message `json:"-"`
	// WorkItem is the Pharos WorkItem we're processing.
	// Not serialized because the Pharos WorkItem record will be
	// more up-to-date and authoritative.
	WorkItem *WorkItem `json:"-"`
	// IntellectualObject is the object we're restoring. Not serialized
	// because if the object has thousands of files, the serialization is
	// huge.
	IntellectualObject *IntellectualObject `json:"-"`
	// PackageSummary contains information about the outcome of the
	// attempt to reassemble this bag for restoration.
	PackageSummary *WorkSummary
	// ValidateSummary contains information about the outcome
	// of validating this newly reassembled bag. We must validate
	// it before sending it to the restoration bucket.
	ValidateSummary *WorkSummary
	// CopySummary contains information about the outcome of the
	// attempt to copy the tarred bag to the depositor's restoration
	// bucket.
	CopySummary *WorkSummary
	// RecordSummary contains information about the outcome of
	// attempts to record the restoration event and the completion
	// of the WorkItem in Pharos.
	RecordSummary *WorkSummary
	// LocalBagDir is the absolute path to the untarred bag. We'll be
	// assembling the bag contents in this directory.
	LocalBagDir string
	// LocalTarFile is the absolute path the tarred version of this
	// bag. The local tar file will not exist until the bag has been
	// fully assembled and tarred.
	LocalTarFile string
	// RestoredToUrl is a URL that points to the copy of this bag
	// in the depositor's S3 restoration bucket.
	RestoredToUrl string
	// CopiedToRestorationAt is a timestamp describing when the
	// reassembled bag was copied to the depositor's S3 restoration
	// bucket.
	CopiedToRestorationAt time.Time
}

// NewRestoreState creates a new RestoreState object with empty
// PackageSummary, RestoreSummary, and ValidationSummary.
func NewRestoreState(message *nsq.Message) *RestoreState {
	return &RestoreState{
		NSQMessage:      message,
		PackageSummary:  NewWorkSummary(),
		ValidateSummary: NewWorkSummary(),
		RecordSummary:   NewWorkSummary(),
		CopySummary:     NewWorkSummary(),
	}
}

// HasErrors returns true if any of the work summaries have errors.
func (restoreState *RestoreState) HasErrors() bool {
	return restoreState.PackageSummary.HasErrors() ||
		restoreState.ValidateSummary.HasErrors() ||
		restoreState.RecordSummary.HasErrors() ||
		restoreState.CopySummary.HasErrors()
}

// HasFatalErrors returns true if any of the work summaries have
// a fatal error.
func (restoreState *RestoreState) HasFatalErrors() bool {
	return (restoreState.PackageSummary.ErrorIsFatal ||
		restoreState.ValidateSummary.ErrorIsFatal ||
		restoreState.RecordSummary.ErrorIsFatal ||
		restoreState.CopySummary.ErrorIsFatal)
}

// AllErrorsAsString returns all error messages from all work summaries
// as a single string.
func (restoreState *RestoreState) AllErrorsAsString() string {
	errors := []string{
		restoreState.PackageSummary.AllErrorsAsString(),
		restoreState.ValidateSummary.AllErrorsAsString(),
		restoreState.RecordSummary.AllErrorsAsString(),
		restoreState.CopySummary.AllErrorsAsString(),
	}
	allErrors := ""
	for _, err := range errors {
		if err != "" {
			allErrors += err + "\n"
		}
	}
	return allErrors
}

// MostRecentSummary returns the WorkSummary for the most recent
// operation.
func (restoreState *RestoreState) MostRecentSummary() *WorkSummary {
	summary := restoreState.PackageSummary
	if restoreState.RecordSummary.Started() {
		summary = restoreState.RecordSummary
	} else if restoreState.CopySummary.Started() {
		summary = restoreState.CopySummary
	} else if restoreState.ValidateSummary.Started() {
		summary = restoreState.ValidateSummary
	}
	return summary
}

// TouchNSQ tells NSQ we're still working on this item.
func (restoreState *RestoreState) TouchNSQ() {
	if restoreState.NSQMessage != nil {
		restoreState.NSQMessage.Touch()
	}
}

// FinishNSQ tells NSQ we're done with this message.
func (restoreState *RestoreState) FinishNSQ() {
	if restoreState.NSQMessage != nil {
		restoreState.NSQMessage.Finish()
	}
}

// RequeueNSQ tells NSQ to give this item to give this item to another
// worker (or perhaps the same worker) after a delay of at least the
// specified number of milliseconds.
func (restoreState *RestoreState) RequeueNSQ(milliseconds int) {
	if restoreState.NSQMessage != nil {
		restoreState.NSQMessage.Requeue(time.Duration(milliseconds) * time.Millisecond)
	}
}
<|endoftext|>